For these exercises, you will be performing differential expression analysis on the same set of rhabdomyosarcoma (RMS) samples we saw in the differential expression instruction notebook. This time, however, you’ll be testing a separate population of cells, the tumor mesoderm cells, for differentially expressed genes between alveolar RMS (ARMS) and embryonal RMS (ERMS) samples.

Before beginning, let’s take a moment to consider the context for this data. Patel et al. (2022) employed scRNA-seq and snRNA-seq to explore the transcriptomic landscape of RMS, with a particular interest in identifying aspects of RMS’ developmental program that can be exploited by therapeutics. One key result from this study was that ERMS featured a clear developmental hierarchy that ARMS lacked, suggesting there may be different contributors to treatment resistance between subtypes. Although development for all RMS samples mirrored normal myogenesis (shown below) with cell types of all stages, ARMS samples had significantly fewer tumor mesoderm cells compared to ERMS samples. Given this difference in abundance, could there also be differences in gene expression in this cell population? We’ll be exploring that question in this notebook!

Figure 1B from Patel et al. (2022)
Figure 1B from Patel et al. (2022)

In this exercise notebook you will:

  • Part A: Prepare single-cell data for differential expression (DE) analysis
  • Part B: Perform DE analysis on the tumor mesoderm cell subpopulation
  • Part C: Compare results to tumor myoblast DE analysis results

Part A: Prepare single-cell data for differential expression (DE) analysis

Read in and prepare the data

To begin, load some libraries that we’ll need for these exercises, including…

  • SingleCellExperiment for working with SCE objects
  • DESeq2 for performing differential expression
  • ggplot2 for plotting
# Load libraries
library(SingleCellExperiment)
library(DESeq2)
library(ggplot2)

Next, set the random seed for reproducibility:

In the next chunk, we’ll set up some file paths, including the path to our RMS data and the output paths for our differential expression analysis results, including both the mesoderm analysis we’ll perform here and the myoblast analysis we performed during instruction.

# data directory for input RMS data
data_dir <- file.path("data", "rms")

# file containing just the samples to use for DE analysis,
#  as created during instruction
rms_sce_file <- file.path(data_dir, "integrated",  "rms_subset_sce.rds")

# ensure analysis results directory has been created
# it should exist already from instruction
deseq_dir <- file.path("analysis", "rms", "deseq")
fs::dir_create(deseq_dir)

# File where we will output results from mesoderm DE analysis
deseq_mesoderm_file <- file.path(deseq_dir, "rms_mesoderm_deseq_results.tsv")

# File where we previously outputted results myoblast DE analysis during
#  instruction
deseq_myoblast_file <- file.path(deseq_dir, "rms_myoblast_deseq_results.tsv")

Now that we’ve defined our file paths, we can read some of it in.

First, let’s read in the deseq_myoblast_file TSV and save it to deseq_myoblast_results. We’ll need this later when we compare mesoderm DE results to myoblast DE results.

# read in myoblast de results for later
deseq_myoblast_results <- readr::read_tsv(deseq_myoblast_file)

Next, read in the SCE file and save it to a variable called rms_sce. Then, print it out to have a look at its contents.

# read in sce object
rms_sce <- readRDS(rms_sce_file)
# print sce
rms_sce
class: SingleCellExperiment 
dim: 60319 26033 
metadata(141): salmon_version reference_index ... miQC_model combined_hvg
assays(3): counts logcounts fastmnn_corrected
rownames(60319): ENSG00000000003 ENSG00000000005 ... ENSG00000288724 ENSG00000288725
rowData names(21): gene_symbol SCPCL000478-mean ... SCPCL000498-mean SCPCL000498-detected
colnames(26033): SCPCL000479-GGGACCTCAAGCGGAT SCPCL000479-CACAGATAGTGAGTGC ... SCPCL000491-TCGCACTAGGAACGTC SCPCL000491-TTGCATTTCAACGCTA
colData names(24): sum detected ... cell_id diagnosis_group
reducedDimNames(4): PCA UMAP fastmnn_PCA fastmnn_UMAP
mainExpName: NULL
altExpNames(0):

Before we proceed, this SCE object contains a colData column sizeFactor that was previously calculated when counts were normalized during initial processing. This column will end up conflicting with some of DESeq2’s operations, so we want to remove this column before proceeding. (Note that if you skipped this step, DESeq2 would end up printing an error about this offending column specifically, so you would have found out later that it has to go!)

# Set column value to `NULL` in order to remove it
rms_sce$sizeFactor <- NULL

As we saw during instruction, this SCE contains three ERMS and three ARMS samples. Let’s remind ourselves of the distribution of cell types before proceeding. To focus on the tumor cell types (as we will be doing for the rest of the notebook!), we’ll also subset the rms_sce object to just tumor cells.

tumor_cells <- rms_sce$celltype_broad |>
  # Find which cell types start with "Tumor_"
  stringr::str_starts("Tumor_") |>
  # Pipe into `which()` to ensure that `NA` values are properly handled
  which()

# filter the SCE object to keep only those cells
rms_sce <- rms_sce[, tumor_cells]

Let’s now plot the distribution of the remaining tumor cell types:

# Create a data frame version of the colData table for plotting
coldata_df <- colData(rms_sce) |>
  as.data.frame()

# Make a barplot of cell type proportions across samples and diagnoses
ggplot(coldata_df) +
  aes(x = sample,
      fill = celltype_broad) +
  geom_bar(color = "black", # Add a thin outline for easier viewing
           lwd = 0.15) +
  facet_wrap(vars(diagnosis_group),
             # only put relevant samples in each panel
             scales = "free_x") +
  # Specify theme and modify x-axis text size
  theme_bw() +
  theme(axis.text.x = element_text(size = 7))

We can see that the proportion of Tumor_Myoblast is much higher than other cell types, including the Tumor_Mesoderm subpopulations we’ll be testing here. We also see that ERMS samples have more Tumor_Mesoderm cells and fewer Tumor_Myocyte cells compared to ARMS samples, consistent with Patel et al.’s findings that ARMS samples tended to have more cell types from later myogenesis developmental stages compared to ERMS. As we proceed to the pseudo-bulking steps for the Tumor_Mesoderm subpopulation, we will check that we have sufficient sample sizes to perform our analysis.

Perform pseudo-bulking

To begin differential expression on single-cell data, we need to pseudo-bulk the data to “collapse” groups of interest. As during instruction, we want to perform pseudo-bulking across two groupings: celltype_broad and sample.

To accomplish this, create a variable called pb_groups, which will be a DataFrame that contains only the colData columns of interest for pseudo-bulking.

# first subset the coldata
# to only have the columns we care about for pseudo-bulking
pb_groups <- colData(rms_sce)[,c("celltype_broad", "sample")]

Next, use this variable to create the pseudo-bulked SCE with the scuttle::aggregateAcrossCells() function, and save it to pb_sce. In addition, you’ll want to ensure column names are properly added back into this new pseudo-bulked object.

# create a new SCE object that contains
# the pseudo-bulked counts across the provided groups
pb_sce <- scuttle::aggregateAcrossCells(
  rms_sce,
  id = pb_groups
)
# Add column names formatted as `{celltype}_{sample}` back to `pb_sce`
colnames(pb_sce) <- glue::glue("{pb_sce$celltype_broad}_{pb_sce$sample}")

# Check
pb_sce
class: SingleCellExperiment 
dim: 60319 18 
metadata(141): salmon_version reference_index ... miQC_model combined_hvg
assays(1): counts
rownames(60319): ENSG00000000003 ENSG00000000005 ... ENSG00000288724 ENSG00000288725
rowData names(21): gene_symbol SCPCL000478-mean ... SCPCL000498-mean SCPCL000498-detected
colnames(18): Tumor_Mesoderm_SCPCL000479 Tumor_Mesoderm_SCPCL000480 ... Tumor_Myocyte_SCPCL000488 Tumor_Myocyte_SCPCL000491
colData names(26): sum detected ... sample ncells
reducedDimNames(4): PCA UMAP fastmnn_PCA fastmnn_UMAP
mainExpName: NULL
altExpNames(0):

Let’s see what we’ve got! Print out pb_sce below. How many “cells” do we have now after pseudo-bulking?

pb_sce
class: SingleCellExperiment 
dim: 60319 18 
metadata(141): salmon_version reference_index ... miQC_model combined_hvg
assays(1): counts
rownames(60319): ENSG00000000003 ENSG00000000005 ... ENSG00000288724 ENSG00000288725
rowData names(21): gene_symbol SCPCL000478-mean ... SCPCL000498-mean SCPCL000498-detected
colnames(18): Tumor_Mesoderm_SCPCL000479 Tumor_Mesoderm_SCPCL000480 ... Tumor_Myocyte_SCPCL000488 Tumor_Myocyte_SCPCL000491
colData names(26): sum detected ... sample ncells
reducedDimNames(4): PCA UMAP fastmnn_PCA fastmnn_UMAP
mainExpName: NULL
altExpNames(0):

As we have seen, when we performed the pseudo-bulking, a new column ncells was added to the colData table. This information tells us how many cells were collapsed into the grouping.

To perform our differential expression analysis on Tumor_Mesoderm cells, we’d like to see a suitable number of cells present in each group. During instruction, we chose a threshold of ncells >= 10 to include a group in analysis. Let’s do the same filtering here, and then check to see that we have enough biological replicates to perform analysis. Recall, we are starting with three ERMS and three ARMS samples, and we’ll need at least three samples from each diagnosis group for our cell type of interest for statistical viability.

To this end, let’s take two filtering steps in the next chunk, saving the output to a new object called mesoderm_pb_sce:

  • We’ll filter the pb_sce to retain only groupings where ncells >= 10
  • We’ll also filter the pbe_sce object to retain only our cell type of interest, Tumor_Mesoderm.
# First filter to keep only `ncells >= 10` and save to `mesoderm_pb_sce`
mesoderm_pb_sce <- pb_sce[, which(pb_sce$ncells >= 10)]

# Then filter to keep only `Tumor_Mesoderm` and save to the same variable
# First create logical vector
mesoderm_cells <- pb_sce$celltype_broad == "Tumor_Mesoderm"

mesoderm_pb_sce <- mesoderm_pb_sce[,mesoderm_cells]

Now let’s check that we have retained enough samples to proceed. In the following chunk, use the table() function on the filtered SCE’s diagnosis_group to count up the number of each diagnosis - hopefully we still have at least three (pseudo-bulked) samples of each!

# Make a table of the diagnosis group counts
table(mesoderm_pb_sce$diagnosis_group)

Indeed, we have enough biological replicates to proceed, so let’s head to differential expression analysis.

Part B: Perform DE analysis on the tumor mesoderm cell subpopulation

Off to (the) differential expression (races)!

Now that we have prepared our object for analysis, we’re ready to identify genes that are differentially expressed between ARMS and ERMS in Tumor_Mesoderm cells.

First, let’s define a DESeqDataSet object called deseq_object from the mesoderm_pb_sce, specifying the design formula across the diagnosis_group variable.

# create the DESeqDataSet object
deseq_object <- DESeqDataSet(
  mesoderm_pb_sce,
  design = ~diagnosis_group
  )

Now, use the convenience function DESeq2::DESeq() to perform the differential expression analysis as we saw during instruction, saving the result back to deseq_object.

# perform differential expression
deseq_object <- DESeq2::DESeq(deseq_object)

Before proceeding, we’ll want to quickly check that the model was a decent fit to the data, and we can do this by visualizing the dispersion estimates using the function DESeq2::plotDispEsts(). Make this plot in the chunk below:

# plot dispersions
DESeq2::plotDispEsts(deseq_object)

How does this plot look to you? A well-fitted model should show decreasing dispersions as the mean along the x-axis increases. Do you see that trend here?

To be able to interpret our results and compare genes, we next need to apply a shrinkage procedure that will correct expression levels to avoid overestimating fold change. Do this in the next chunk by taking the following steps:

  • Extract the results which pass a threshold of alpha = 0.05 from deseq_object. Save this data frame to a variable deseq_results.
  • Perform the shrinkage procedure using DESeq2::lfcShrink(), saving results to shrink_results. Make sure to look at your model first to find the position of the result coefficient to provide to DESeq2::lfcShrink().
# GEW: I'm curious about 2 things:
  # 1) To what extent cell proportion / count impacts DE results -- subset to 400 random cells (Tumor_mesoderm) per diagnosis group, and repeat analysis
  # 2) To what extent shrinkage impacts DE results -- repeat visualizations on the non-shrunk results
# Extract results into a data frame
deseq_results <- DESeq2::results(deseq_object, alpha = 0.05)

# Before performing the shrinkage procedure, check
#  here to find where the result coefficient is
DESeq2::resultsNames(deseq_object)
[1] "Intercept"                    "diagnosis_group_ERMS_vs_ARMS"
# Perform the shrinkage procedure, saving to `shrink_results`
shrink_results <- DESeq2::lfcShrink(
  deseq_object,
  res = deseq_results,
  coef = 2,
  type = "apeglm"
  )

# Compare
head(deseq_results)
log2 fold change (MLE): diagnosis group ERMS vs ARMS 
Wald test p-value: diagnosis group ERMS vs ARMS 
DataFrame with 6 rows and 6 columns
                 baseMean log2FoldChange     lfcSE      stat    pvalue      padj
                <numeric>      <numeric> <numeric> <numeric> <numeric> <numeric>
ENSG00000000003  8.713223       0.649856  0.986484  0.658760  0.510050  0.820792
ENSG00000000005  0.730183       1.068391  2.932840  0.364286  0.715645        NA
ENSG00000000419 30.176049       0.225685  0.602494  0.374584  0.707970  0.908990
ENSG00000000457 19.815192      -0.296812  0.759054 -0.391029  0.695776  0.903512
ENSG00000000460 27.162696       0.610321  0.655246  0.931439  0.351626  0.712684
ENSG00000000938  0.544495       0.496746  2.671682  0.185930  0.852500        NA
head(shrink_results)
log2 fold change (MAP): diagnosis group ERMS vs ARMS 
Wald test p-value: diagnosis group ERMS vs ARMS 
DataFrame with 6 rows and 5 columns
                 baseMean log2FoldChange     lfcSE    pvalue      padj
                <numeric>      <numeric> <numeric> <numeric> <numeric>
ENSG00000000003  8.713223      0.1855118  0.557615  0.510050  0.820792
ENSG00000000005  0.730183      0.0665971  0.619273  0.715645        NA
ENSG00000000419 30.176049      0.1181973  0.440999  0.707970  0.908990
ENSG00000000457 19.815192     -0.1203426  0.489972  0.695776  0.903512
ENSG00000000460 27.162696      0.3128435  0.491756  0.351626  0.712684
ENSG00000000938  0.544495      0.0527327  0.614362  0.852500        NA

Go ahead and print the resulting shrink_results:

shrink_results

Before proceeding, it’s worth discussing how to interpret these log2FoldChange values in terms of ARMS and ERMS samples. In this case, we did not set a particular factor order for the diagnosis_group variable that we tested on, so R by default set the order alphabetically. We can see this below using levels().

# See the order of the diagnosis_group column
levels(mesoderm_pb_sce$diagnosis_group)
[1] "ARMS" "ERMS"

This means that all results are relative to ARMS. Negative log2FoldChange values indicate that ERMS has lower expression relative to ARMS. Positive log2FoldChange values indicate that ERMS has higher expression relative to ARMS.

Something else we’ll need to help us interpret these results are some gene names, since not all of us have had a chance to memorize the Ensembl IDs yet. So, let’s now add in a gene_symbol column to our results data frame (shrink_results). These symbols were originally stored in the SCE’s rowData table so we can simply grab the names from there. In the chunk below, we’ll do some wrangling to combine shrink_results with the gene names, saving the final result to deseq_mesoderm_results.

# Create a data frame from the SCE rowData
sce_rowdata_df <- rowData(mesoderm_pb_sce) |>
   # Convert to a data frame and move the rownames
   #  into a new column called `ensembl_id`
   tibble::as_tibble(rownames = "ensembl_id")

# Join the DESeq results with `sce_rowdata_df`
deseq_mesoderm_results <- shrink_results |>
  tibble::as_tibble(rownames = "ensembl_id") |>
  # join with sce_rowdata_df by Ensembl ids
  dplyr::left_join(sce_rowdata_df, by = "ensembl_id") |>
  # reorder columns so gene_symbol comes right after ensembl_id
  #  to make the table easier to read
  dplyr::relocate(gene_symbol, .after = "ensembl_id")

# Print the final results data frame
deseq_mesoderm_results

Let’s now filter to only our significant results, using a cutoff of adjusted P-value of 0.05, and save the new data frame to deseq_mesoderm_results_sig

# Filter results to padj <= 0.05
deseq_mesoderm_results_sig <- deseq_mesoderm_results[which(deseq_mesoderm_results$padj <= 0.05),]
# Print the result
deseq_mesoderm_results_sig
# Another method using dplyr
deseq_mesoderm_results_sig2 <- deseq_mesoderm_results |>
  dplyr::filter(padj <= 0.05)

deseq_mesoderm_results_sig2

Visualize results

Let’s visualize some of our results!

To start, use the EnhancedVolcano package to make a volcano plot of the results. For this plot, specify that points be labeled with the gene symbol and use the default P-value cutoff of 1e-05. Feel free to apply any additional styles you want (the plot itself is a ggplot2 object!). Hint: Make sure to plot the deseq_mesoderm_results data frame that contains both significant and not significant results.

# Make an (enhanced) volcano plot
shrinkage_volcano_plot <- EnhancedVolcano::EnhancedVolcano(
  deseq_mesoderm_results,
  x = "log2FoldChange",
  y = "padj",
  lab = deseq_mesoderm_results$gene_symbol,
  pCutoff = 1e-05, # p value cutoff (default)
  drawConnectors = TRUE,
  labSize = 3,
  title = "DESeq2 Tumor Mesoderm (With Shrinkage)", # no title
  subtitle = NULL, # or subtitle
  caption = NULL, # or caption
) +
  theme_classic() +
  theme(legend.position = "bottom")

shrinkage_volcano_plot
ggsave(glue::glue("{deseq_dir}/volcano_shrinkage.png"), dpi=400, height=5, width=5)

Let’s pick some highly differentially expressed genes from this volcano plot and show them on a UMAP. We’ll pick one gene that shows higher ARMS expression (negative log2 fold changes), and one that shows higher ERMS expression (positive log2 fold changes). To plot these on a UMAP, we’ll need to know their Ensembl ids, so let’s get that information first:

# Define some genes to visualize
arms_gene <- "HMCN2"
erms_gene <- "PLEKHB2"

# Use dplyr to get the associated Ensembl ids
deseq_mesoderm_results_sig |>
  # Find rows that contain our gene symbols
  dplyr::filter(gene_symbol %in% c(erms_gene, arms_gene)) |>
  # Select only these two columns
  dplyr::select(ensembl_id, gene_symbol)

To make our UMAP of mesoderm cells, we also want to filter our original rms_sce to contain only Tumor_Mesoderm cells. (An aside! You may recall we previously created mesoderm_pb_sce that was filtered to tumor mesoderm cells, so why can’t we use that object to make a UMAP? That object was derived from the pseudo-bulked SCE, so it does not contain individual cells. Therefore, we need to go back to the beginning and work with rms_sce.)

# Filter rms_sce to contain only Tumor_Mesoderm cells
mesoderm_sce <- rms_sce[,which(rms_sce$celltype_broad == 'Tumor_Mesoderm')]

We should also check the names of our reduced dimensions to see what the UMAP matrix is called so that we can plot it:

# Print reducedDimNames of mesoderm_sce
reducedDimNames(mesoderm_sce)
[1] "PCA"          "UMAP"         "fastmnn_PCA"  "fastmnn_UMAP"

We’d like to plot the integrated UMAP, so we’ll plot the "fastmnn_UMAP" matrix below.

I map, you map, we all map for map maps! First, let’s visualize the gene with Ensembl id ENSG00000148357 that is upregulated in ARMS. For this plot, color by expression and facet by diagnosis_group.

# Plot UMAP showing ENSG00000148357 expression across diagnosis groups
scater::plotUMAP(
  mesoderm_sce,
  dimred = "fastmnn_UMAP",
  color_by = "ENSG00000148357",
  other_fields = "diagnosis_group"
) +
  theme_bw() +
  facet_grid(cols = vars(diagnosis_group))

How does this plot look to you? Does it seem to reflect the statistical result that ARMS has higher expression, on average, compared to ERMS?

Let’s do the same with the gene that is upregulated in ERMS, and again think about whether the plot shows the trends you expected based on the differential expression results!

# GEW: What's the difference between facet_wrap() and facet_grid() ?
# Plot UMAP showing ENSG00000115762 expression across diagnosis groups
scater::plotReducedDim(mesoderm_sce,
                       dimred = "fastmnn_UMAP",
                       color_by = "ENSG00000115762",
                       other_fields = "diagnosis_group") +
  facet_wrap(vars(diagnosis_group)) +
  theme_bw()

We can also visualize this data using violin plots with the scater::plotExpression() function, allowing us to compare expression across subpopulations of interest (here, tumor cell types and diagnosis groups).

Let’s make that plot below from the rms_sce object, which you’ll recall we previously filtered to only tumor cells. Use facet_grid() in your plot below to show separate panels for each combination of cell type and diagnosis group.

# Define a vector of Ensembl ids to plot

# Create faceted violin plot

Based on this plot, it looks like the expression differences among diagnosis groups we found for tumor mesoderm cells could be an overall trend across cancer cell types. If we wanted to formally test this, we could consider a multivariate model that accounts for both cell type and diagnosis group at once, rather than performing a separately testing each cell type.

Let’s go ahead and save the deseq_mesoderm_results data frame to our output TSV file.

# Write out the results TSV file

Part C: Compare results to tumor myoblast DE analysis results

During instruction, we performed differential expression analysis on tumor myoblast cells to detect differences between ARMS and ERMS samples. We might wonder how our results for tumor mesoderm cells compare to those for myoblasts. Before we compare some of these findings, we need to be aware of a few caveats:

  • As mentioned above, if we were really interested in whether ARMS and ERMS samples have different expression across any group of cell types, we would have liked to start with a multivariate model. By performing separate tests for separate cell types, we risk making comparisons based on “significance,” for example concluding “this gene was significant in mesoderm but not myoblast, therefore it’s only important in mesoderm cell types.” Significance as a property in-and-of-itself relies on arbitrary P-value cutoffs, and P-values across different tests and samples are not created equally! This means you do not want to rely on these kinds of comparisons to draw conclusions, but they can be useful for exploration, as we will do here.

  • During all of our DE tests, there’s a good chance that we may have committed some light circularity: The cell types we are using from Patel et al. (2022) were determined from expression data. But then we used these expression-derived cell types to filter for DE testing, and then we tested for differences in, you guessed it, expression! It’s not necessarily possible to avoid this trap (since we need to use something to annotate cell types), but it’s important to be aware that this is a confounding factor across our results.

Bearing these caveats in mind, let’s go ahead and compare results a bit. At the beginning of this notebook, we read in the tumor myoblast DE results and saved it as deseq_myoblast_results. Let’s remind ourselves what this data frame looks like:

head(deseq_myoblast_results)

The data frame has the same columns as our mesoderm results stored in deseq_mesoderm_results. To compare results more easily, we’d like to have them all in a single data frame formatted to facilitate comparison. For example, we can make a data frame that has columns for each test’s P-values and log2-fold changes so we can directly compare the results for each gene. Let’s wrangle that together:

# select from and rename myoblast results
myoblast_df <- deseq_myoblast_results |>
  dplyr::select(ensembl_id,
                gene_symbol,
                myoblast_padj = padj,
                myoblast_log2FoldChange = log2FoldChange)

# select & rename mesoderm result
mesoderm_df <- deseq_mesoderm_results |>
  dplyr::select(ensembl_id,
                gene_symbol,
                mesoderm_padj = padj,
                mesoderm_log2FoldChange = log2FoldChange)

# Combine myoblast and mesoderm results
deseq_results_all <- dplyr::inner_join(myoblast_df, mesoderm_df)

# Print the combined result data frame
deseq_results_all

We can now directly compare results on a per-gene basis! One way we can explore the result differences is with a scatterplot showing log2-fold changes for each cell type. We can also style the points to be colored based on their overall significance, using one color for each of the following conditions:

  • Both cell types are significant
  • Only myoblast tumor cells are significant
  • Only mesoderm tumor cells are significant
  • Neither cell type is significant

Coloring points by significance in ggplot2 will require a bit more wrangling, for which we’ll use the function dplyr::case_when(). We provide this function with a set of logical expressions and each assigned value is designated by ~. The expressions are evaluated in order, stopping at the first one that evaluates as TRUE and returning the associated value. We’ll use dplyr::case_when() to help us create a new significance_type column in our data frame to contain this information.

deseq_results_all <- deseq_results_all |>
  # Create a new column called significance_type
  dplyr::mutate(significance_type = dplyr::case_when(
    # Use value "Both" if both comparisons are significant
    myoblast_padj <= 0.05 & mesoderm_padj <= 0.05 ~ "Both",
    # Use value "Myoblast only" if only myoblast is significant
    myoblast_padj <= 0.05 ~ "Myoblast only",
    # Use value "Mesoderm only" if only mesoderm is significant
    mesoderm_padj <= 0.05 ~ "Mesoderm only",
    # Default case: neither comparison is significant
    #  This will capture all remaining rows not covered by
    #  the previous conditions
    TRUE ~ "Neither"
  )) |>
  # Move the new column to the front of the data frame
  #  so we can easily see it when printing
  dplyr::select(significance_type, everything())

# Print the updated data frame
deseq_results_all

Let’s have a quick look at the new significance_type variable by making a table() of its values:

# Make a table of significance types

Most genes are not significant in either comparison, and the myoblast test detected more significant genes compared to the mesoderm test. But there is still quite a bit of overlap in the genes identified as significant in both tests!

Now we can make our scatterplot:

# Plot log2 fold changes against each other
ggplot(deseq_results_all,
       aes(x = myoblast_log2FoldChange,
           y = mesoderm_log2FoldChange,
           color = significance_type)) +
  # Styling to see points
  geom_point(size = 0.5,
             alpha = 0.5) +
  # Use the colorblind-friendly viridis color scale
  scale_color_viridis_d() +
  # Set plot labels
  labs(
    x = "Myoblast log2 fold change",
    y = "Mesoderm log2 fold change",
    color = "Signficance Type"
  ) +
  theme_bw() +
  # Make legend points larger and easier to see
  guides(color = guide_legend(override.aes = list(size = 2))) +
  # Set equal x and y scales for easier comparison between axes
  coord_equal()

What kind of trends do we see here?

  • The middle of the plot close to coordinates (0,0) has a large cloud of yellow “Neither” points, which makes sense.
  • Along each line x = 0 or y = 0, we see strips of points for genes that are only significant for only one cell type. For example, the blue “Mesoderm only” points mostly follow x = 0; for these points, the associated myoblast log2-fold changes are all at about 0.
  • Finally, we have the purple “Both” points, which are present in each quadrant farther away from (0,0), deemed significant in both DE tests. Most of these points are in the top right and bottom left quadrants, corresponding to conditions where both cell types show significant upregulation or downregulation, respectively. However, we do also see some purple points, albeit fewer, in the top left and bottom right quadrants which represent genes where both cell types show significant expression differences but in different directions.

To start exploring, let’s pick out some of the genes with the largest fold-changes from the top right and bottom left quadrants of the plot. Based on the plot, it looks like we’ll find a set of shared upregulated genes by looking for expression values that are greater than 8 for both cell types, and downregulated genes by looking for expression values less than -10 for myoblasts and -7 for mesoderm cells.

# Find genes in top right and bottom left quadrants
deseq_results_all |>
  dplyr::filter(
    # Find either the highly upregulated genes:
    (myoblast_log2FoldChange >= 8 & mesoderm_log2FoldChange >= 8) |
     # OR, the highly downregulated genes:
    (myoblast_log2FoldChange <= -10 & mesoderm_log2FoldChange <=-7)
  ) |>
  # arrange on myoblast_log2FoldChange
  dplyr::arrange(myoblast_log2FoldChange)

Something you’ll see in these results are some pretty different P-values between cell types (also note that the NA genes here are lncRNAs with no formally assigned gene symbol). Specifically, the myoblast P-values are all 3-7 orders of magnitude lower than their mesoderm counterparts, which may be a result of the relatively higher sample size for myoblast tests - larger sample sizes lead to more extreme P-values. Importantly, we do not want to compare these P-values directly and conclude that a given gene was “more or less significant” in one cell type or another, since P-values cannot be compared across tests (again, for a more robust assessment of differential expression, use a multivariate model that accounts for cell types!).

To wrap up, feel free to perform some quick visualization of some of these genes!

Alternatively (or in addition!), you can visualize the differential expression of genes that regulate myogenesis, as shown in the figure from Patel et al. at the top of this notebook. Let’s have a quick look at those results:

# Define vector of myogenesis gene symbols
myogenesis_symbols <- c("MEOX2", "PAX3", "MYF5", "MSC", "MYOG", "MEF2C")

# Print the DE results for those genes
deseq_results_all |>
  dplyr::filter(gene_symbol %in% myogenesis_symbols) |>
  # sort in order of the `myogenesis_symbols` vector
  dplyr::arrange(match(gene_symbol, myogenesis_symbols))

You can make plots we’ve seen like…

  • A faceted UMAP with scater::plotUMAP() showing expression across cell type and diagnosis group
  • A violin plot with scater::plotExpression() faceted across cell type and diagnosis group
# Use this chunk (or add more chunks!) to make your own plots

# vector of gene symbols named by Ensembl id

Session Info

sessionInfo()
LS0tCnRpdGxlOiAiU2luZ2xlIENlbGwgRXhlcmNpc2U6IERpZmZlcmVudGlhbCBFeHByZXNzaW9uIEFuYWx5c2lzIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCmVkaXRvcl9vcHRpb25zOgogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgoKRm9yIHRoZXNlIGV4ZXJjaXNlcywgeW91IHdpbGwgYmUgcGVyZm9ybWluZyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBvbiB0aGUgc2FtZSBzZXQgb2YgcmhhYmRvbXlvc2FyY29tYSAoUk1TKSBzYW1wbGVzIHdlIHNhdyBpbiB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gaW5zdHJ1Y3Rpb24gbm90ZWJvb2suClRoaXMgdGltZSwgaG93ZXZlciwgeW91J2xsIGJlIHRlc3RpbmcgYSBzZXBhcmF0ZSBwb3B1bGF0aW9uIG9mIGNlbGxzLCB0aGUgdHVtb3IgbWVzb2Rlcm0gY2VsbHMsIGZvciBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgYmV0d2VlbiBhbHZlb2xhciBSTVMgKEFSTVMpIGFuZCBlbWJyeW9uYWwgUk1TIChFUk1TKSBzYW1wbGVzLgoKCkJlZm9yZSBiZWdpbm5pbmcsIGxldCdzIHRha2UgYSBtb21lbnQgdG8gY29uc2lkZXIgdGhlIGNvbnRleHQgZm9yIHRoaXMgZGF0YS4KW1BhdGVsIF9ldCBhbC5fICgyMDIyKV0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMTYvai5kZXZjZWwuMjAyMi4wNC4wMDMpIGVtcGxveWVkIHNjUk5BLXNlcSBhbmQgc25STkEtc2VxIHRvIGV4cGxvcmUgdGhlIHRyYW5zY3JpcHRvbWljIGxhbmRzY2FwZSBvZiBSTVMsIHdpdGggYSBwYXJ0aWN1bGFyIGludGVyZXN0IGluIGlkZW50aWZ5aW5nIGFzcGVjdHMgb2YgUk1TJyBkZXZlbG9wbWVudGFsIHByb2dyYW0gdGhhdCBjYW4gYmUgZXhwbG9pdGVkIGJ5IHRoZXJhcGV1dGljcy4KT25lIGtleSByZXN1bHQgZnJvbSB0aGlzIHN0dWR5IHdhcyB0aGF0IEVSTVMgZmVhdHVyZWQgYSBjbGVhciBkZXZlbG9wbWVudGFsIGhpZXJhcmNoeSB0aGF0IEFSTVMgbGFja2VkLCBzdWdnZXN0aW5nIHRoZXJlIG1heSBiZSBkaWZmZXJlbnQgY29udHJpYnV0b3JzIHRvIHRyZWF0bWVudCByZXNpc3RhbmNlIGJldHdlZW4gc3VidHlwZXMuCkFsdGhvdWdoIGRldmVsb3BtZW50IGZvciBhbGwgUk1TIHNhbXBsZXMgbWlycm9yZWQgbm9ybWFsIG15b2dlbmVzaXMgKHNob3duIGJlbG93KSB3aXRoIGNlbGwgdHlwZXMgb2YgYWxsIHN0YWdlcywgQVJNUyBzYW1wbGVzIGhhZCBzaWduaWZpY2FudGx5IGZld2VyIHR1bW9yIG1lc29kZXJtIGNlbGxzIGNvbXBhcmVkIHRvIEVSTVMgc2FtcGxlcy4KR2l2ZW4gdGhpcyBkaWZmZXJlbmNlIGluIGFidW5kYW5jZSwgY291bGQgdGhlcmUgYWxzbyBiZSBkaWZmZXJlbmNlcyBpbiBnZW5lIGV4cHJlc3Npb24gaW4gdGhpcyBjZWxsIHBvcHVsYXRpb24/CldlJ2xsIGJlIGV4cGxvcmluZyB0aGF0IHF1ZXN0aW9uIGluIHRoaXMgbm90ZWJvb2shCgoKIVtGaWd1cmUgMUIgZnJvbSBQYXRlbCBfZXQgYWwuXyAoMjAyMildKGRpYWdyYW1zL3BhdGVsXzIwMjJfZmlnMUIucG5nKQoKCkluIHRoaXMgZXhlcmNpc2Ugbm90ZWJvb2sgeW91IHdpbGw6CgotIFBhcnQgQTogUHJlcGFyZSBzaW5nbGUtY2VsbCBkYXRhIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiAoREUpIGFuYWx5c2lzCi0gUGFydCBCOiBQZXJmb3JtIERFIGFuYWx5c2lzIG9uIHRoZSB0dW1vciBtZXNvZGVybSBjZWxsIHN1YnBvcHVsYXRpb24KLSBQYXJ0IEM6IENvbXBhcmUgcmVzdWx0cyB0byB0dW1vciBteW9ibGFzdCBERSBhbmFseXNpcyByZXN1bHRzCgoKIyMgUGFydCBBOiBQcmVwYXJlIHNpbmdsZS1jZWxsIGRhdGEgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIChERSkgYW5hbHlzaXMKCiMjIyBSZWFkIGluIGFuZCBwcmVwYXJlIHRoZSBkYXRhCgpUbyBiZWdpbiwgbG9hZCBzb21lIGxpYnJhcmllcyB0aGF0IHdlJ2xsIG5lZWQgZm9yIHRoZXNlIGV4ZXJjaXNlcywgaW5jbHVkaW5nLi4uCgotIGBTaW5nbGVDZWxsRXhwZXJpbWVudGAgZm9yIHdvcmtpbmcgd2l0aCBTQ0Ugb2JqZWN0cwotIGBERVNlcTJgIGZvciBwZXJmb3JtaW5nIGRpZmZlcmVudGlhbCBleHByZXNzaW9uCi0gYGdncGxvdDJgIGZvciBwbG90dGluZwoKYGBge3IgbG9hZCBsaWJyYXJpZXMsIHNvbHV0aW9uID0gVFJVRX0KIyBMb2FkIGxpYnJhcmllcwpsaWJyYXJ5KFNpbmdsZUNlbGxFeHBlcmltZW50KQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeShnZ3Bsb3QyKQpgYGAKCk5leHQsIHNldCB0aGUgcmFuZG9tIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eToKCmBgYHtyIHNldCBzZWVkLCBzb2x1dGlvbiA9IFRSVUV9CiMgU2V0IHRoZSByYW5kb20gc2VlZApzZXQuc2VlZCgyOTgzKQpgYGAKCkluIHRoZSBuZXh0IGNodW5rLCB3ZSdsbCBzZXQgdXAgc29tZSBmaWxlIHBhdGhzLCBpbmNsdWRpbmcgdGhlIHBhdGggdG8gb3VyIFJNUyBkYXRhIGFuZCB0aGUgb3V0cHV0IHBhdGhzIGZvciBvdXIgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgcmVzdWx0cywgaW5jbHVkaW5nIGJvdGggdGhlIG1lc29kZXJtIGFuYWx5c2lzIHdlJ2xsIHBlcmZvcm0gaGVyZSBhbmQgdGhlIG15b2JsYXN0IGFuYWx5c2lzIHdlIHBlcmZvcm1lZCBkdXJpbmcgaW5zdHJ1Y3Rpb24uCgoKYGBge3IgZmlsZXBhdGhzfQojIGRhdGEgZGlyZWN0b3J5IGZvciBpbnB1dCBSTVMgZGF0YQpkYXRhX2RpciA8LSBmaWxlLnBhdGgoImRhdGEiLCAicm1zIikKCiMgZmlsZSBjb250YWluaW5nIGp1c3QgdGhlIHNhbXBsZXMgdG8gdXNlIGZvciBERSBhbmFseXNpcywKIyAgYXMgY3JlYXRlZCBkdXJpbmcgaW5zdHJ1Y3Rpb24Kcm1zX3NjZV9maWxlIDwtIGZpbGUucGF0aChkYXRhX2RpciwgImludGVncmF0ZWQiLCAgInJtc19zdWJzZXRfc2NlLnJkcyIpCgojIGVuc3VyZSBhbmFseXNpcyByZXN1bHRzIGRpcmVjdG9yeSBoYXMgYmVlbiBjcmVhdGVkCiMgaXQgc2hvdWxkIGV4aXN0IGFscmVhZHkgZnJvbSBpbnN0cnVjdGlvbgpkZXNlcV9kaXIgPC0gZmlsZS5wYXRoKCJhbmFseXNpcyIsICJybXMiLCAiZGVzZXEiKQpmczo6ZGlyX2NyZWF0ZShkZXNlcV9kaXIpCgojIEZpbGUgd2hlcmUgd2Ugd2lsbCBvdXRwdXQgcmVzdWx0cyBmcm9tIG1lc29kZXJtIERFIGFuYWx5c2lzCmRlc2VxX21lc29kZXJtX2ZpbGUgPC0gZmlsZS5wYXRoKGRlc2VxX2RpciwgInJtc19tZXNvZGVybV9kZXNlcV9yZXN1bHRzLnRzdiIpCgojIEZpbGUgd2hlcmUgd2UgcHJldmlvdXNseSBvdXRwdXR0ZWQgcmVzdWx0cyBteW9ibGFzdCBERSBhbmFseXNpcyBkdXJpbmcKIyAgaW5zdHJ1Y3Rpb24KZGVzZXFfbXlvYmxhc3RfZmlsZSA8LSBmaWxlLnBhdGgoZGVzZXFfZGlyLCAicm1zX215b2JsYXN0X2Rlc2VxX3Jlc3VsdHMudHN2IikKYGBgCgoKTm93IHRoYXQgd2UndmUgZGVmaW5lZCBvdXIgZmlsZSBwYXRocywgd2UgY2FuIHJlYWQgc29tZSBvZiBpdCBpbi4KCkZpcnN0LCBsZXQncyByZWFkIGluIHRoZSBgZGVzZXFfbXlvYmxhc3RfZmlsZWAgVFNWIGFuZCBzYXZlIGl0IHRvIGBkZXNlcV9teW9ibGFzdF9yZXN1bHRzYC4KV2UnbGwgbmVlZCB0aGlzIGxhdGVyIHdoZW4gd2UgY29tcGFyZSBtZXNvZGVybSBERSByZXN1bHRzIHRvIG15b2JsYXN0IERFIHJlc3VsdHMuCgpgYGB7ciByZWFkIG15b2JsYXN0IFRTViwgc29sdXRpb24gPSBUUlVFfQojIHJlYWQgaW4gbXlvYmxhc3QgZGUgcmVzdWx0cyBmb3IgbGF0ZXIKZGVzZXFfbXlvYmxhc3RfcmVzdWx0cyA8LSByZWFkcjo6cmVhZF90c3YoZGVzZXFfbXlvYmxhc3RfZmlsZSkKYGBgCgpOZXh0LCByZWFkIGluIHRoZSBTQ0UgZmlsZSBhbmQgc2F2ZSBpdCB0byBhIHZhcmlhYmxlIGNhbGxlZCBgcm1zX3NjZWAuClRoZW4sIHByaW50IGl0IG91dCB0byBoYXZlIGEgbG9vayBhdCBpdHMgY29udGVudHMuCgpgYGB7ciByZWFkIGlucHV0IHNjZSwgc29sdXRpb24gPSBUUlVFfQojIHJlYWQgaW4gc2NlIG9iamVjdApybXNfc2NlIDwtIHJlYWRSRFMocm1zX3NjZV9maWxlKQojIHByaW50IHNjZQpybXNfc2NlCmBgYAoKQmVmb3JlIHdlIHByb2NlZWQsIHRoaXMgU0NFIG9iamVjdCBjb250YWlucyBhIGBjb2xEYXRhYCBjb2x1bW4gYHNpemVGYWN0b3JgIHRoYXQgd2FzIHByZXZpb3VzbHkgY2FsY3VsYXRlZCB3aGVuIGNvdW50cyB3ZXJlIG5vcm1hbGl6ZWQgZHVyaW5nIGluaXRpYWwgcHJvY2Vzc2luZy4KVGhpcyBjb2x1bW4gd2lsbCBlbmQgdXAgY29uZmxpY3Rpbmcgd2l0aCBzb21lIG9mIGBERVNlcTJgJ3Mgb3BlcmF0aW9ucywgc28gd2Ugd2FudCB0byByZW1vdmUgdGhpcyBjb2x1bW4gYmVmb3JlIHByb2NlZWRpbmcuCihOb3RlIHRoYXQgaWYgeW91IHNraXBwZWQgdGhpcyBzdGVwLCBgREVTZXEyYCB3b3VsZCBlbmQgdXAgcHJpbnRpbmcgYW4gZXJyb3IgYWJvdXQgdGhpcyBvZmZlbmRpbmcgY29sdW1uIHNwZWNpZmljYWxseSwgc28geW91IHdvdWxkIGhhdmUgZm91bmQgb3V0IGxhdGVyIHRoYXQgaXQgaGFzIHRvIGdvISkKCmBgYHtyIHJlbW92ZSBzaXplRmFjdG9ycyBjb2x1bW59CiMgU2V0IGNvbHVtbiB2YWx1ZSB0byBgTlVMTGAgaW4gb3JkZXIgdG8gcmVtb3ZlIGl0CnJtc19zY2Ukc2l6ZUZhY3RvciA8LSBOVUxMCmBgYAoKQXMgd2Ugc2F3IGR1cmluZyBpbnN0cnVjdGlvbiwgdGhpcyBTQ0UgY29udGFpbnMgdGhyZWUgRVJNUyBhbmQgdGhyZWUgQVJNUyBzYW1wbGVzLgpMZXQncyByZW1pbmQgb3Vyc2VsdmVzIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgY2VsbCB0eXBlcyBiZWZvcmUgcHJvY2VlZGluZy4KVG8gZm9jdXMgb24gdGhlIHR1bW9yIGNlbGwgdHlwZXMgKGFzIHdlIHdpbGwgYmUgZG9pbmcgZm9yIHRoZSByZXN0IG9mIHRoZSBub3RlYm9vayEpLCB3ZSdsbCBhbHNvIHN1YnNldCB0aGUgYHJtc19zY2VgIG9iamVjdCB0byBqdXN0IHR1bW9yIGNlbGxzLgoKYGBge3IgZmlsdGVyIHR1bW9yIHNjZX0KdHVtb3JfY2VsbHMgPC0gcm1zX3NjZSRjZWxsdHlwZV9icm9hZCB8PgogICMgRmluZCB3aGljaCBjZWxsIHR5cGVzIHN0YXJ0IHdpdGggIlR1bW9yXyIKICBzdHJpbmdyOjpzdHJfc3RhcnRzKCJUdW1vcl8iKSB8PgogICMgUGlwZSBpbnRvIGB3aGljaCgpYCB0byBlbnN1cmUgdGhhdCBgTkFgIHZhbHVlcyBhcmUgcHJvcGVybHkgaGFuZGxlZAogIHdoaWNoKCkKCiMgZmlsdGVyIHRoZSBTQ0Ugb2JqZWN0IHRvIGtlZXAgb25seSB0aG9zZSBjZWxscwpybXNfc2NlIDwtIHJtc19zY2VbLCB0dW1vcl9jZWxsc10KYGBgCgpMZXQncyBub3cgcGxvdCB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSByZW1haW5pbmcgdHVtb3IgY2VsbCB0eXBlczoKCmBgYHtyIGNlbGx0eXBlIGJhcnBsb3R9CiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB2ZXJzaW9uIG9mIHRoZSBjb2xEYXRhIHRhYmxlIGZvciBwbG90dGluZwpjb2xkYXRhX2RmIDwtIGNvbERhdGEocm1zX3NjZSkgfD4KICBhcy5kYXRhLmZyYW1lKCkKCiMgTWFrZSBhIGJhcnBsb3Qgb2YgY2VsbCB0eXBlIHByb3BvcnRpb25zIGFjcm9zcyBzYW1wbGVzIGFuZCBkaWFnbm9zZXMKZ2dwbG90KGNvbGRhdGFfZGYpICsKICBhZXMoeCA9IHNhbXBsZSwKICAgICAgZmlsbCA9IGNlbGx0eXBlX2Jyb2FkKSArCiAgZ2VvbV9iYXIoY29sb3IgPSAiYmxhY2siLCAjIEFkZCBhIHRoaW4gb3V0bGluZSBmb3IgZWFzaWVyIHZpZXdpbmcKICAgICAgICAgICBsd2QgPSAwLjE1KSArCiAgZmFjZXRfd3JhcCh2YXJzKGRpYWdub3Npc19ncm91cCksCiAgICAgICAgICAgICAjIG9ubHkgcHV0IHJlbGV2YW50IHNhbXBsZXMgaW4gZWFjaCBwYW5lbAogICAgICAgICAgICAgc2NhbGVzID0gImZyZWVfeCIpICsKICAjIFNwZWNpZnkgdGhlbWUgYW5kIG1vZGlmeSB4LWF4aXMgdGV4dCBzaXplCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpKQpgYGAKCldlIGNhbiBzZWUgdGhhdCB0aGUgcHJvcG9ydGlvbiBvZiBgVHVtb3JfTXlvYmxhc3RgIGlzIG11Y2ggaGlnaGVyIHRoYW4gb3RoZXIgY2VsbCB0eXBlcywgaW5jbHVkaW5nIHRoZSBgVHVtb3JfTWVzb2Rlcm1gIHN1YnBvcHVsYXRpb25zIHdlJ2xsIGJlIHRlc3RpbmcgaGVyZS4KV2UgYWxzbyBzZWUgdGhhdCBFUk1TIHNhbXBsZXMgaGF2ZSBtb3JlIGBUdW1vcl9NZXNvZGVybWAgY2VsbHMgYW5kIGZld2VyIGBUdW1vcl9NeW9jeXRlYCBjZWxscyBjb21wYXJlZCB0byBBUk1TIHNhbXBsZXMsIGNvbnNpc3RlbnQgd2l0aCBQYXRlbCBfZXQgYWwuXydzIGZpbmRpbmdzIHRoYXQgQVJNUyBzYW1wbGVzIHRlbmRlZCB0byBoYXZlIG1vcmUgY2VsbCB0eXBlcyBmcm9tIGxhdGVyIG15b2dlbmVzaXMgZGV2ZWxvcG1lbnRhbCBzdGFnZXMgY29tcGFyZWQgdG8gRVJNUy4KQXMgd2UgcHJvY2VlZCB0byB0aGUgcHNldWRvLWJ1bGtpbmcgc3RlcHMgZm9yIHRoZSBgVHVtb3JfTWVzb2Rlcm1gIHN1YnBvcHVsYXRpb24sIHdlIHdpbGwgY2hlY2sgdGhhdCB3ZSBoYXZlIHN1ZmZpY2llbnQgc2FtcGxlIHNpemVzIHRvIHBlcmZvcm0gb3VyIGFuYWx5c2lzLgoKCiMjIyBQZXJmb3JtIHBzZXVkby1idWxraW5nCgpUbyBiZWdpbiBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvbiBzaW5nbGUtY2VsbCBkYXRhLCB3ZSBuZWVkIHRvIHBzZXVkby1idWxrIHRoZSBkYXRhIHRvICJjb2xsYXBzZSIgZ3JvdXBzIG9mIGludGVyZXN0LgpBcyBkdXJpbmcgaW5zdHJ1Y3Rpb24sIHdlIHdhbnQgdG8gcGVyZm9ybSBwc2V1ZG8tYnVsa2luZyBhY3Jvc3MgdHdvIGdyb3VwaW5nczogYGNlbGx0eXBlX2Jyb2FkYCBhbmQgYHNhbXBsZWAuCgpUbyBhY2NvbXBsaXNoIHRoaXMsIGNyZWF0ZSBhIHZhcmlhYmxlIGNhbGxlZCBgcGJfZ3JvdXBzYCwgd2hpY2ggd2lsbCBiZSBhIGBEYXRhRnJhbWVgIHRoYXQgY29udGFpbnMgb25seSB0aGUgYGNvbERhdGFgIGNvbHVtbnMgb2YgaW50ZXJlc3QgZm9yIHBzZXVkby1idWxraW5nLgoKYGBge3IgY3JlYXRlIHBiX2dyb3Vwcywgc29sdXRpb24gPSBUUlVFfQojIGZpcnN0IHN1YnNldCB0aGUgY29sZGF0YQojIHRvIG9ubHkgaGF2ZSB0aGUgY29sdW1ucyB3ZSBjYXJlIGFib3V0IGZvciBwc2V1ZG8tYnVsa2luZwpwYl9ncm91cHMgPC0gY29sRGF0YShybXNfc2NlKVssYygiY2VsbHR5cGVfYnJvYWQiLCAic2FtcGxlIildCmBgYAoKTmV4dCwgdXNlIHRoaXMgdmFyaWFibGUgdG8gY3JlYXRlIHRoZSBwc2V1ZG8tYnVsa2VkIFNDRSB3aXRoIHRoZSBgc2N1dHRsZTo6YWdncmVnYXRlQWNyb3NzQ2VsbHMoKWAgZnVuY3Rpb24sIGFuZCBzYXZlIGl0IHRvIGBwYl9zY2VgLgpJbiBhZGRpdGlvbiwgeW91J2xsIHdhbnQgdG8gZW5zdXJlIGNvbHVtbiBuYW1lcyBhcmUgcHJvcGVybHkgYWRkZWQgYmFjayBpbnRvIHRoaXMgbmV3IHBzZXVkby1idWxrZWQgb2JqZWN0LgoKYGBge3IgY3JlYXRlIHBiX3NjZSwgc29sdXRpb24gPSBUUlVFfQojIGNyZWF0ZSBhIG5ldyBTQ0Ugb2JqZWN0IHRoYXQgY29udGFpbnMKIyB0aGUgcHNldWRvLWJ1bGtlZCBjb3VudHMgYWNyb3NzIHRoZSBwcm92aWRlZCBncm91cHMKcGJfc2NlIDwtIHNjdXR0bGU6OmFnZ3JlZ2F0ZUFjcm9zc0NlbGxzKAogIHJtc19zY2UsCiAgaWQgPSBwYl9ncm91cHMKKQojIEFkZCBjb2x1bW4gbmFtZXMgZm9ybWF0dGVkIGFzIGB7Y2VsbHR5cGV9X3tzYW1wbGV9YCBiYWNrIHRvIGBwYl9zY2VgCmNvbG5hbWVzKHBiX3NjZSkgPC0gZ2x1ZTo6Z2x1ZSgie3BiX3NjZSRjZWxsdHlwZV9icm9hZH1fe3BiX3NjZSRzYW1wbGV9IikKCiMgQ2hlY2sKcGJfc2NlCmBgYAoKTGV0J3Mgc2VlIHdoYXQgd2UndmUgZ290IQpQcmludCBvdXQgYHBiX3NjZWAgYmVsb3cuCkhvdyBtYW55ICJjZWxscyIgZG8gd2UgaGF2ZSBub3cgYWZ0ZXIgcHNldWRvLWJ1bGtpbmc/CgpgYGB7ciBwcmludCBwYl9zY2V9CnBiX3NjZQpgYGAKCkFzIHdlIGhhdmUgc2Vlbiwgd2hlbiB3ZSBwZXJmb3JtZWQgdGhlIHBzZXVkby1idWxraW5nLCBhIG5ldyBjb2x1bW4gYG5jZWxsc2Agd2FzIGFkZGVkIHRvIHRoZSBgY29sRGF0YWAgdGFibGUuClRoaXMgaW5mb3JtYXRpb24gdGVsbHMgdXMgaG93IG1hbnkgY2VsbHMgd2VyZSBjb2xsYXBzZWQgaW50byB0aGUgZ3JvdXBpbmcuCgpUbyBwZXJmb3JtIG91ciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBvbiBgVHVtb3JfTWVzb2Rlcm1gIGNlbGxzLCB3ZSdkIGxpa2UgdG8gc2VlIGEgc3VpdGFibGUgbnVtYmVyIG9mIGNlbGxzIHByZXNlbnQgaW4gZWFjaCBncm91cC4KRHVyaW5nIGluc3RydWN0aW9uLCB3ZSBjaG9zZSBhIHRocmVzaG9sZCBvZiBgbmNlbGxzID49IDEwYCB0byBpbmNsdWRlIGEgZ3JvdXAgaW4gYW5hbHlzaXMuCkxldCdzIGRvIHRoZSBzYW1lIGZpbHRlcmluZyBoZXJlLCBhbmQgdGhlbiBjaGVjayB0byBzZWUgdGhhdCB3ZSBoYXZlIGVub3VnaCBiaW9sb2dpY2FsIHJlcGxpY2F0ZXMgdG8gcGVyZm9ybSBhbmFseXNpcy4KUmVjYWxsLCB3ZSBhcmUgc3RhcnRpbmcgd2l0aCB0aHJlZSBFUk1TIGFuZCB0aHJlZSBBUk1TIHNhbXBsZXMsIGFuZCB3ZSdsbCBuZWVkIGF0IGxlYXN0IHRocmVlIHNhbXBsZXMgZnJvbSBlYWNoIGRpYWdub3NpcyBncm91cCBmb3Igb3VyIGNlbGwgdHlwZSBvZiBpbnRlcmVzdCBmb3Igc3RhdGlzdGljYWwgdmlhYmlsaXR5LgoKVG8gdGhpcyBlbmQsIGxldCdzIHRha2UgdHdvIGZpbHRlcmluZyBzdGVwcyBpbiB0aGUgbmV4dCBjaHVuaywgc2F2aW5nIHRoZSBvdXRwdXQgdG8gYSBuZXcgb2JqZWN0IGNhbGxlZCBgbWVzb2Rlcm1fcGJfc2NlYDoKCi0gV2UnbGwgZmlsdGVyIHRoZSBgcGJfc2NlYCB0byByZXRhaW4gb25seSBncm91cGluZ3Mgd2hlcmUgYG5jZWxscyA+PSAxMGAKLSBXZSdsbCBhbHNvIGZpbHRlciB0aGUgYHBiZV9zY2VgIG9iamVjdCB0byByZXRhaW4gb25seSBvdXIgY2VsbCB0eXBlIG9mIGludGVyZXN0LCBgVHVtb3JfTWVzb2Rlcm1gLgoKYGBge3IgZmlsdGVyIHBiX3NjZSwgc29sdXRpb24gPSBUUlVFfQojIEZpcnN0IGZpbHRlciB0byBrZWVwIG9ubHkgYG5jZWxscyA+PSAxMGAgYW5kIHNhdmUgdG8gYG1lc29kZXJtX3BiX3NjZWAKbWVzb2Rlcm1fcGJfc2NlIDwtIHBiX3NjZVssIHdoaWNoKHBiX3NjZSRuY2VsbHMgPj0gMTApXQoKIyBUaGVuIGZpbHRlciB0byBrZWVwIG9ubHkgYFR1bW9yX01lc29kZXJtYCBhbmQgc2F2ZSB0byB0aGUgc2FtZSB2YXJpYWJsZQojIEZpcnN0IGNyZWF0ZSBsb2dpY2FsIHZlY3RvcgptZXNvZGVybV9jZWxscyA8LSBwYl9zY2UkY2VsbHR5cGVfYnJvYWQgPT0gIlR1bW9yX01lc29kZXJtIgoKbWVzb2Rlcm1fcGJfc2NlIDwtIG1lc29kZXJtX3BiX3NjZVssbWVzb2Rlcm1fY2VsbHNdCmBgYAoKTm93IGxldCdzIGNoZWNrIHRoYXQgd2UgaGF2ZSByZXRhaW5lZCBlbm91Z2ggc2FtcGxlcyB0byBwcm9jZWVkLgpJbiB0aGUgZm9sbG93aW5nIGNodW5rLCB1c2UgdGhlIGB0YWJsZSgpYCBmdW5jdGlvbiBvbiB0aGUgZmlsdGVyZWQgU0NFJ3MgYGRpYWdub3Npc19ncm91cGAgdG8gY291bnQgdXAgdGhlIG51bWJlciBvZiBlYWNoIGRpYWdub3NpcyAtIGhvcGVmdWxseSB3ZSBzdGlsbCBoYXZlIGF0IGxlYXN0IHRocmVlIChwc2V1ZG8tYnVsa2VkKSBzYW1wbGVzIG9mIGVhY2ghCgpgYGB7ciB0YWJsZSBkaWFnbm9zaXNfZ3JvdXAsIHNvbHV0aW9uID0gVFJVRX0KIyBNYWtlIGEgdGFibGUgb2YgdGhlIGRpYWdub3NpcyBncm91cCBjb3VudHMKdGFibGUobWVzb2Rlcm1fcGJfc2NlJGRpYWdub3Npc19ncm91cCkKYGBgCgpJbmRlZWQsIHdlIGhhdmUgZW5vdWdoIGJpb2xvZ2ljYWwgcmVwbGljYXRlcyB0byBwcm9jZWVkLCBzbyBsZXQncyBoZWFkIHRvIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzLgoKIyMgUGFydCBCOiBQZXJmb3JtIERFIGFuYWx5c2lzIG9uIHRoZSB0dW1vciBtZXNvZGVybSBjZWxsIHN1YnBvcHVsYXRpb24KCk9mZiB0byAodGhlKSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiAocmFjZXMpIQoKTm93IHRoYXQgd2UgaGF2ZSBwcmVwYXJlZCBvdXIgb2JqZWN0IGZvciBhbmFseXNpcywgd2UncmUgcmVhZHkgdG8gaWRlbnRpZnkgZ2VuZXMgdGhhdCBhcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gQVJNUyBhbmQgRVJNUyBpbiBgVHVtb3JfTWVzb2Rlcm1gIGNlbGxzLgoKRmlyc3QsIGxldCdzIGRlZmluZSBhIGBERVNlcURhdGFTZXRgIG9iamVjdCBjYWxsZWQgYGRlc2VxX29iamVjdGAgZnJvbSB0aGUgYG1lc29kZXJtX3BiX3NjZWAsIHNwZWNpZnlpbmcgdGhlIGRlc2lnbiBmb3JtdWxhIGFjcm9zcyB0aGUgYGRpYWdub3Npc19ncm91cGAgdmFyaWFibGUuCgpgYGB7ciBkZWZpbmUgZGVzZXEgb2JqZWN0LCBzb2x1dGlvbiA9IFRSVUV9CiMgY3JlYXRlIHRoZSBERVNlcURhdGFTZXQgb2JqZWN0CmRlc2VxX29iamVjdCA8LSBERVNlcURhdGFTZXQoCiAgbWVzb2Rlcm1fcGJfc2NlLAogIGRlc2lnbiA9IH5kaWFnbm9zaXNfZ3JvdXAKICApCmBgYAoKTm93LCB1c2UgdGhlIGNvbnZlbmllbmNlIGZ1bmN0aW9uIGBERVNlcTI6OkRFU2VxKClgIHRvIHBlcmZvcm0gdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIGFzIHdlIHNhdyBkdXJpbmcgaW5zdHJ1Y3Rpb24sIHNhdmluZyB0aGUgcmVzdWx0IGJhY2sgdG8gYGRlc2VxX29iamVjdGAuCgpgYGB7ciBwZXJmb3JtIGRlIGFuYWx5c2lzLCBzb2x1dGlvbiA9IFRSVUV9CiMgcGVyZm9ybSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbgpkZXNlcV9vYmplY3QgPC0gREVTZXEyOjpERVNlcShkZXNlcV9vYmplY3QpCmBgYAoKQmVmb3JlIHByb2NlZWRpbmcsIHdlJ2xsIHdhbnQgdG8gcXVpY2tseSBjaGVjayB0aGF0IHRoZSBtb2RlbCB3YXMgYSBkZWNlbnQgZml0IHRvIHRoZSBkYXRhLCBhbmQgd2UgY2FuIGRvIHRoaXMgYnkgdmlzdWFsaXppbmcgdGhlIGRpc3BlcnNpb24gZXN0aW1hdGVzIHVzaW5nIHRoZSBmdW5jdGlvbiBgREVTZXEyOjpwbG90RGlzcEVzdHMoKWAuCk1ha2UgdGhpcyBwbG90IGluIHRoZSBjaHVuayBiZWxvdzoKCmBgYHtyIHBsb3QgZGlzcGVyc2lvbnMsIHNvbHV0aW9uID0gVFJVRX0KIyBwbG90IGRpc3BlcnNpb25zCkRFU2VxMjo6cGxvdERpc3BFc3RzKGRlc2VxX29iamVjdCkKYGBgCgpIb3cgZG9lcyB0aGlzIHBsb3QgbG9vayB0byB5b3U/CkEgd2VsbC1maXR0ZWQgbW9kZWwgc2hvdWxkIHNob3cgZGVjcmVhc2luZyBkaXNwZXJzaW9ucyBhcyB0aGUgbWVhbiBhbG9uZyB0aGUgeC1heGlzIGluY3JlYXNlcy4KRG8geW91IHNlZSB0aGF0IHRyZW5kIGhlcmU/CgpUbyBiZSBhYmxlIHRvIGludGVycHJldCBvdXIgcmVzdWx0cyBhbmQgY29tcGFyZSBnZW5lcywgd2UgbmV4dCBuZWVkIHRvIGFwcGx5IGEgc2hyaW5rYWdlIHByb2NlZHVyZSB0aGF0IHdpbGwgY29ycmVjdCBleHByZXNzaW9uIGxldmVscyB0byBhdm9pZCBvdmVyZXN0aW1hdGluZyBmb2xkIGNoYW5nZS4KRG8gdGhpcyBpbiB0aGUgbmV4dCBjaHVuayBieSB0YWtpbmcgdGhlIGZvbGxvd2luZyBzdGVwczoKCi0gRXh0cmFjdCB0aGUgcmVzdWx0cyB3aGljaCBwYXNzIGEgdGhyZXNob2xkIG9mIGBhbHBoYSA9IDAuMDVgIGZyb20gYGRlc2VxX29iamVjdGAuClNhdmUgdGhpcyBkYXRhIGZyYW1lIHRvIGEgdmFyaWFibGUgYGRlc2VxX3Jlc3VsdHNgLgotIFBlcmZvcm0gdGhlIHNocmlua2FnZSBwcm9jZWR1cmUgdXNpbmcgYERFU2VxMjo6bGZjU2hyaW5rKClgLCBzYXZpbmcgcmVzdWx0cyB0byBgc2hyaW5rX3Jlc3VsdHNgLgpNYWtlIHN1cmUgdG8gbG9vayBhdCB5b3VyIG1vZGVsIGZpcnN0IHRvIGZpbmQgdGhlIHBvc2l0aW9uIG9mIHRoZSByZXN1bHQgY29lZmZpY2llbnQgdG8gcHJvdmlkZSB0byBgREVTZXEyOjpsZmNTaHJpbmsoKWAuCgpgYGB7cn0KIyBHRVc6IEknbSBjdXJpb3VzIGFib3V0IDIgdGhpbmdzOgogICMgMSkgVG8gd2hhdCBleHRlbnQgY2VsbCBwcm9wb3J0aW9uIC8gY291bnQgaW1wYWN0cyBERSByZXN1bHRzIC0tIHN1YnNldCB0byA0MDAgcmFuZG9tIGNlbGxzIChUdW1vcl9tZXNvZGVybSkgcGVyIGRpYWdub3NpcyBncm91cCwgYW5kIHJlcGVhdCBhbmFseXNpcwogICMgMikgVG8gd2hhdCBleHRlbnQgc2hyaW5rYWdlIGltcGFjdHMgREUgcmVzdWx0cyAtLSByZXBlYXQgdmlzdWFsaXphdGlvbnMgb24gdGhlIG5vbi1zaHJ1bmsgcmVzdWx0cwpgYGAKCmBgYHtyIHBlcmZvcm0gc2hyaW5rYWdlLCBzb2x1dGlvbiA9IFRSVUV9CiMgRXh0cmFjdCByZXN1bHRzIGludG8gYSBkYXRhIGZyYW1lCmRlc2VxX3Jlc3VsdHMgPC0gREVTZXEyOjpyZXN1bHRzKGRlc2VxX29iamVjdCwgYWxwaGEgPSAwLjA1KQoKIyBCZWZvcmUgcGVyZm9ybWluZyB0aGUgc2hyaW5rYWdlIHByb2NlZHVyZSwgY2hlY2sKIyAgaGVyZSB0byBmaW5kIHdoZXJlIHRoZSByZXN1bHQgY29lZmZpY2llbnQgaXMKREVTZXEyOjpyZXN1bHRzTmFtZXMoZGVzZXFfb2JqZWN0KQoKIyBQZXJmb3JtIHRoZSBzaHJpbmthZ2UgcHJvY2VkdXJlLCBzYXZpbmcgdG8gYHNocmlua19yZXN1bHRzYApzaHJpbmtfcmVzdWx0cyA8LSBERVNlcTI6OmxmY1NocmluaygKICBkZXNlcV9vYmplY3QsCiAgcmVzID0gZGVzZXFfcmVzdWx0cywKICBjb2VmID0gMiwKICB0eXBlID0gImFwZWdsbSIKICApCgojIENvbXBhcmUKaGVhZChkZXNlcV9yZXN1bHRzKQpoZWFkKHNocmlua19yZXN1bHRzKQpgYGAKCkdvIGFoZWFkIGFuZCBwcmludCB0aGUgcmVzdWx0aW5nIGBzaHJpbmtfcmVzdWx0c2A6CgpgYGB7ciBwcmludCBzaHJpbmtfcmVzdWx0c30Kc2hyaW5rX3Jlc3VsdHMKYGBgCgpCZWZvcmUgcHJvY2VlZGluZywgaXQncyB3b3J0aCBkaXNjdXNzaW5nIGhvdyB0byBpbnRlcnByZXQgdGhlc2UgYGxvZzJGb2xkQ2hhbmdlYCB2YWx1ZXMgaW4gdGVybXMgb2YgQVJNUyBhbmQgRVJNUyBzYW1wbGVzLgpJbiB0aGlzIGNhc2UsIHdlIGRpZCBub3Qgc2V0IGEgcGFydGljdWxhciBmYWN0b3Igb3JkZXIgZm9yIHRoZSBgZGlhZ25vc2lzX2dyb3VwYCB2YXJpYWJsZSB0aGF0IHdlIHRlc3RlZCBvbiwgc28gUiBieSBkZWZhdWx0IHNldCB0aGUgb3JkZXIgYWxwaGFiZXRpY2FsbHkuCldlIGNhbiBzZWUgdGhpcyBiZWxvdyB1c2luZyBgbGV2ZWxzKClgLgoKYGBge3IgY2hlY2sgZGlhZ25vc2lzX2dyb3VwIGxldmVsc30KIyBTZWUgdGhlIG9yZGVyIG9mIHRoZSBkaWFnbm9zaXNfZ3JvdXAgY29sdW1uCmxldmVscyhtZXNvZGVybV9wYl9zY2UkZGlhZ25vc2lzX2dyb3VwKQpgYGAKClRoaXMgbWVhbnMgdGhhdCBhbGwgcmVzdWx0cyBhcmUgX3JlbGF0aXZlIHRvIEFSTVNfLgpOZWdhdGl2ZSBgbG9nMkZvbGRDaGFuZ2VgIHZhbHVlcyBpbmRpY2F0ZSB0aGF0IEVSTVMgaGFzIGxvd2VyIGV4cHJlc3Npb24gcmVsYXRpdmUgdG8gQVJNUy4KUG9zaXRpdmUgYGxvZzJGb2xkQ2hhbmdlYCB2YWx1ZXMgaW5kaWNhdGUgdGhhdCBFUk1TIGhhcyBoaWdoZXIgZXhwcmVzc2lvbiByZWxhdGl2ZSB0byBBUk1TLgoKU29tZXRoaW5nIGVsc2Ugd2UnbGwgbmVlZCB0byBoZWxwIHVzIGludGVycHJldCB0aGVzZSByZXN1bHRzIGFyZSBzb21lIGdlbmUgbmFtZXMsIHNpbmNlIG5vdCBhbGwgb2YgdXMgaGF2ZSBoYWQgYSBjaGFuY2UgdG8gbWVtb3JpemUgdGhlIEVuc2VtYmwgSURzIHlldC4KU28sIGxldCdzIG5vdyBhZGQgaW4gYSBgZ2VuZV9zeW1ib2xgIGNvbHVtbiB0byBvdXIgcmVzdWx0cyBkYXRhIGZyYW1lIChgc2hyaW5rX3Jlc3VsdHNgKS4KVGhlc2Ugc3ltYm9scyB3ZXJlIG9yaWdpbmFsbHkgc3RvcmVkIGluIHRoZSBTQ0UncyBgcm93RGF0YWAgdGFibGUgc28gd2UgY2FuIHNpbXBseSBncmFiIHRoZSBuYW1lcyBmcm9tIHRoZXJlLgpJbiB0aGUgY2h1bmsgYmVsb3csIHdlJ2xsIGRvIHNvbWUgd3JhbmdsaW5nIHRvIGNvbWJpbmUgYHNocmlua19yZXN1bHRzYCB3aXRoIHRoZSBnZW5lIG5hbWVzLCBzYXZpbmcgdGhlIGZpbmFsIHJlc3VsdCB0byBgZGVzZXFfbWVzb2Rlcm1fcmVzdWx0c2AuCgpgYGB7ciBhZGQgZ2VuZV9zeW1ib2x9CiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSBmcm9tIHRoZSBTQ0Ugcm93RGF0YQpzY2Vfcm93ZGF0YV9kZiA8LSByb3dEYXRhKG1lc29kZXJtX3BiX3NjZSkgfD4KICAgIyBDb252ZXJ0IHRvIGEgZGF0YSBmcmFtZSBhbmQgbW92ZSB0aGUgcm93bmFtZXMKICAgIyAgaW50byBhIG5ldyBjb2x1bW4gY2FsbGVkIGBlbnNlbWJsX2lkYAogICB0aWJibGU6OmFzX3RpYmJsZShyb3duYW1lcyA9ICJlbnNlbWJsX2lkIikKCiMgSm9pbiB0aGUgREVTZXEgcmVzdWx0cyB3aXRoIGBzY2Vfcm93ZGF0YV9kZmAKZGVzZXFfbWVzb2Rlcm1fcmVzdWx0cyA8LSBzaHJpbmtfcmVzdWx0cyB8PgogIHRpYmJsZTo6YXNfdGliYmxlKHJvd25hbWVzID0gImVuc2VtYmxfaWQiKSB8PgogICMgam9pbiB3aXRoIHNjZV9yb3dkYXRhX2RmIGJ5IEVuc2VtYmwgaWRzCiAgZHBseXI6OmxlZnRfam9pbihzY2Vfcm93ZGF0YV9kZiwgYnkgPSAiZW5zZW1ibF9pZCIpIHw+CiAgIyByZW9yZGVyIGNvbHVtbnMgc28gZ2VuZV9zeW1ib2wgY29tZXMgcmlnaHQgYWZ0ZXIgZW5zZW1ibF9pZAogICMgIHRvIG1ha2UgdGhlIHRhYmxlIGVhc2llciB0byByZWFkCiAgZHBseXI6OnJlbG9jYXRlKGdlbmVfc3ltYm9sLCAuYWZ0ZXIgPSAiZW5zZW1ibF9pZCIpCgojIFByaW50IHRoZSBmaW5hbCByZXN1bHRzIGRhdGEgZnJhbWUKZGVzZXFfbWVzb2Rlcm1fcmVzdWx0cwpgYGAKCgpMZXQncyBub3cgZmlsdGVyIHRvIG9ubHkgb3VyIHNpZ25pZmljYW50IHJlc3VsdHMsIHVzaW5nIGEgY3V0b2ZmIG9mIF9hZGp1c3RlZCBQLXZhbHVlXyBvZiAwLjA1LCBhbmQgc2F2ZSB0aGUgbmV3IGRhdGEgZnJhbWUgdG8gYGRlc2VxX21lc29kZXJtX3Jlc3VsdHNfc2lnYAoKYGBge3IgZmlsdGVyIG1lc29kZXJtIHNpZ25pZmljYW50LCBzb2x1dGlvbiA9IFRSVUV9CiMgRmlsdGVyIHJlc3VsdHMgdG8gcGFkaiA8PSAwLjA1CmRlc2VxX21lc29kZXJtX3Jlc3VsdHNfc2lnIDwtIGRlc2VxX21lc29kZXJtX3Jlc3VsdHNbd2hpY2goZGVzZXFfbWVzb2Rlcm1fcmVzdWx0cyRwYWRqIDw9IDAuMDUpLF0KIyBQcmludCB0aGUgcmVzdWx0CmRlc2VxX21lc29kZXJtX3Jlc3VsdHNfc2lnCmBgYAoKYGBge3J9CiMgQW5vdGhlciBtZXRob2QgdXNpbmcgZHBseXIKZGVzZXFfbWVzb2Rlcm1fcmVzdWx0c19zaWcyIDwtIGRlc2VxX21lc29kZXJtX3Jlc3VsdHMgfD4KICBkcGx5cjo6ZmlsdGVyKHBhZGogPD0gMC4wNSkKCmRlc2VxX21lc29kZXJtX3Jlc3VsdHNfc2lnMgpgYGAKCiMjIyBWaXN1YWxpemUgcmVzdWx0cwoKTGV0J3MgdmlzdWFsaXplIHNvbWUgb2Ygb3VyIHJlc3VsdHMhCgpUbyBzdGFydCwgdXNlIHRoZSBgRW5oYW5jZWRWb2xjYW5vYCBwYWNrYWdlIHRvIG1ha2UgYSB2b2xjYW5vIHBsb3Qgb2YgdGhlIHJlc3VsdHMuCkZvciB0aGlzIHBsb3QsIHNwZWNpZnkgdGhhdCBwb2ludHMgYmUgbGFiZWxlZCB3aXRoIHRoZSBnZW5lIHN5bWJvbCBhbmQgdXNlIHRoZSBkZWZhdWx0IFAtdmFsdWUgY3V0b2ZmIG9mIGAxZS0wNWAuCkZlZWwgZnJlZSB0byBhcHBseSBhbnkgYWRkaXRpb25hbCBzdHlsZXMgeW91IHdhbnQgKHRoZSBwbG90IGl0c2VsZiBpcyBhIGBnZ3Bsb3QyYCBvYmplY3QhKS4KSGludDogTWFrZSBzdXJlIHRvIHBsb3QgdGhlIGBkZXNlcV9tZXNvZGVybV9yZXN1bHRzYCBkYXRhIGZyYW1lIHRoYXQgY29udGFpbnMgYm90aCBzaWduaWZpY2FudCBhbmQgbm90IHNpZ25pZmljYW50IHJlc3VsdHMuCgpgYGB7ciBtZXNvZGVybSB2b2xjYW5vIHBsb3QsIHNvbHV0aW9uID0gVFJVRX0KIyBNYWtlIGFuIChlbmhhbmNlZCkgdm9sY2FubyBwbG90CnNocmlua2FnZV92b2xjYW5vX3Bsb3QgPC0gRW5oYW5jZWRWb2xjYW5vOjpFbmhhbmNlZFZvbGNhbm8oCiAgZGVzZXFfbWVzb2Rlcm1fcmVzdWx0cywKICB4ID0gImxvZzJGb2xkQ2hhbmdlIiwKICB5ID0gInBhZGoiLAogIGxhYiA9IGRlc2VxX21lc29kZXJtX3Jlc3VsdHMkZ2VuZV9zeW1ib2wsCiAgcEN1dG9mZiA9IDFlLTA1LCAjIHAgdmFsdWUgY3V0b2ZmIChkZWZhdWx0KQogIGRyYXdDb25uZWN0b3JzID0gVFJVRSwKICBsYWJTaXplID0gMywKICB0aXRsZSA9ICJERVNlcTIgVHVtb3IgTWVzb2Rlcm0gKFdpdGggU2hyaW5rYWdlKSIsICMgbm8gdGl0bGUKICBzdWJ0aXRsZSA9IE5VTEwsICMgb3Igc3VidGl0bGUKICBjYXB0aW9uID0gTlVMTCwgIyBvciBjYXB0aW9uCikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCgpzaHJpbmthZ2Vfdm9sY2Fub19wbG90Cmdnc2F2ZShnbHVlOjpnbHVlKCJ7ZGVzZXFfZGlyfS92b2xjYW5vX3Nocmlua2FnZS5wbmciKSwgZHBpPTQwMCwgaGVpZ2h0PTUsIHdpZHRoPTUpCmBgYAoKTGV0J3MgcGljayBzb21lIGhpZ2hseSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgZnJvbSB0aGlzIHZvbGNhbm8gcGxvdCBhbmQgc2hvdyB0aGVtIG9uIGEgVU1BUC4KV2UnbGwgcGljayBvbmUgZ2VuZSB0aGF0IHNob3dzIGhpZ2hlciBBUk1TIGV4cHJlc3Npb24gKG5lZ2F0aXZlIGxvZzIgZm9sZCBjaGFuZ2VzKSwgYW5kIG9uZSB0aGF0IHNob3dzIGhpZ2hlciBFUk1TIGV4cHJlc3Npb24gKHBvc2l0aXZlIGxvZzIgZm9sZCBjaGFuZ2VzKS4KVG8gcGxvdCB0aGVzZSBvbiBhIFVNQVAsIHdlJ2xsIG5lZWQgdG8ga25vdyB0aGVpciBFbnNlbWJsIGlkcywgc28gbGV0J3MgZ2V0IHRoYXQgaW5mb3JtYXRpb24gZmlyc3Q6CgoKYGBge3IgZmluZCBlbnNlbWJsIGlkc30KIyBEZWZpbmUgc29tZSBnZW5lcyB0byB2aXN1YWxpemUKYXJtc19nZW5lIDwtICJITUNOMiIKZXJtc19nZW5lIDwtICJQTEVLSEIyIgoKIyBVc2UgZHBseXIgdG8gZ2V0IHRoZSBhc3NvY2lhdGVkIEVuc2VtYmwgaWRzCmRlc2VxX21lc29kZXJtX3Jlc3VsdHNfc2lnIHw+CiAgIyBGaW5kIHJvd3MgdGhhdCBjb250YWluIG91ciBnZW5lIHN5bWJvbHMKICBkcGx5cjo6ZmlsdGVyKGdlbmVfc3ltYm9sICVpbiUgYyhlcm1zX2dlbmUsIGFybXNfZ2VuZSkpIHw+CiAgIyBTZWxlY3Qgb25seSB0aGVzZSB0d28gY29sdW1ucwogIGRwbHlyOjpzZWxlY3QoZW5zZW1ibF9pZCwgZ2VuZV9zeW1ib2wpCmBgYAoKVG8gbWFrZSBvdXIgVU1BUCBvZiBtZXNvZGVybSBjZWxscywgd2UgYWxzbyB3YW50IHRvIGZpbHRlciBvdXIgb3JpZ2luYWwgYHJtc19zY2VgIHRvIGNvbnRhaW4gb25seSBgVHVtb3JfTWVzb2Rlcm1gIGNlbGxzLgooQW4gYXNpZGUhCllvdSBtYXkgcmVjYWxsIHdlIHByZXZpb3VzbHkgY3JlYXRlZCBgbWVzb2Rlcm1fcGJfc2NlYCB0aGF0IHdhcyBmaWx0ZXJlZCB0byB0dW1vciBtZXNvZGVybSBjZWxscywgc28gd2h5IGNhbid0IHdlIHVzZSB0aGF0IG9iamVjdCB0byBtYWtlIGEgVU1BUD8KVGhhdCBvYmplY3Qgd2FzIGRlcml2ZWQgZnJvbSB0aGUgX3BzZXVkby1idWxrZWRfIFNDRSwgc28gaXQgZG9lcyBub3QgY29udGFpbiBpbmRpdmlkdWFsIGNlbGxzLgpUaGVyZWZvcmUsIHdlIG5lZWQgdG8gZ28gYmFjayB0byB0aGUgYmVnaW5uaW5nIGFuZCB3b3JrIHdpdGggYHJtc19zY2VgLikKCmBgYHtyIGZpbHRlciBybXNfc2NlLCBzb2x1dGlvbiA9IFRSVUV9CiMgRmlsdGVyIHJtc19zY2UgdG8gY29udGFpbiBvbmx5IFR1bW9yX01lc29kZXJtIGNlbGxzCm1lc29kZXJtX3NjZSA8LSBybXNfc2NlWyx3aGljaChybXNfc2NlJGNlbGx0eXBlX2Jyb2FkID09ICdUdW1vcl9NZXNvZGVybScpXQpgYGAKCldlIHNob3VsZCBhbHNvIGNoZWNrIHRoZSBuYW1lcyBvZiBvdXIgcmVkdWNlZCBkaW1lbnNpb25zIHRvIHNlZSB3aGF0IHRoZSBVTUFQIG1hdHJpeCBpcyBjYWxsZWQgc28gdGhhdCB3ZSBjYW4gcGxvdCBpdDoKCmBgYHtyIGZpbmQgdW1hcCBuYW1lfQojIFByaW50IHJlZHVjZWREaW1OYW1lcyBvZiBtZXNvZGVybV9zY2UKcmVkdWNlZERpbU5hbWVzKG1lc29kZXJtX3NjZSkKYGBgCgpXZSdkIGxpa2UgdG8gcGxvdCB0aGUgaW50ZWdyYXRlZCBVTUFQLCBzbyB3ZSdsbCBwbG90IHRoZSBgImZhc3Rtbm5fVU1BUCJgIG1hdHJpeCBiZWxvdy4KCkkgbWFwLCB5b3UgbWFwLCB3ZSBhbGwgbWFwIGZvciBtYXAgbWFwcyEKRmlyc3QsIGxldCdzIHZpc3VhbGl6ZSB0aGUgZ2VuZSB3aXRoIEVuc2VtYmwgaWQgYEVOU0cwMDAwMDE0ODM1N2AgdGhhdCBpcyB1cHJlZ3VsYXRlZCBpbiBBUk1TLgpGb3IgdGhpcyBwbG90LCBjb2xvciBieSBleHByZXNzaW9uIGFuZCBmYWNldCBieSBgZGlhZ25vc2lzX2dyb3VwYC4KCmBgYHtyIHVtYXAgYXJtcyBnZW5lLCBzb2x1dGlvbiA9IFRSVUV9CiMgUGxvdCBVTUFQIHNob3dpbmcgRU5TRzAwMDAwMTQ4MzU3IGV4cHJlc3Npb24gYWNyb3NzIGRpYWdub3NpcyBncm91cHMKc2NhdGVyOjpwbG90VU1BUCgKICBtZXNvZGVybV9zY2UsCiAgZGltcmVkID0gImZhc3Rtbm5fVU1BUCIsCiAgY29sb3JfYnkgPSAiRU5TRzAwMDAwMTQ4MzU3IiwKICBvdGhlcl9maWVsZHMgPSAiZGlhZ25vc2lzX2dyb3VwIgopICsKICB0aGVtZV9idygpICsKICBmYWNldF9ncmlkKGNvbHMgPSB2YXJzKGRpYWdub3Npc19ncm91cCkpCmBgYAoKSG93IGRvZXMgdGhpcyBwbG90IGxvb2sgdG8geW91PwpEb2VzIGl0IHNlZW0gdG8gcmVmbGVjdCB0aGUgc3RhdGlzdGljYWwgcmVzdWx0IHRoYXQgQVJNUyBoYXMgaGlnaGVyIGV4cHJlc3Npb24sIG9uIGF2ZXJhZ2UsIGNvbXBhcmVkIHRvIEVSTVM/CgpMZXQncyBkbyB0aGUgc2FtZSB3aXRoIHRoZSBnZW5lIHRoYXQgaXMgdXByZWd1bGF0ZWQgaW4gRVJNUywgYW5kIGFnYWluIHRoaW5rIGFib3V0IHdoZXRoZXIgdGhlIHBsb3Qgc2hvd3MgdGhlIHRyZW5kcyB5b3UgZXhwZWN0ZWQgYmFzZWQgb24gdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHJlc3VsdHMhCgpgYGB7cn0KIyBHRVc6IFdoYXQncyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGZhY2V0X3dyYXAoKSBhbmQgZmFjZXRfZ3JpZCgpID8KYGBgCgpgYGB7ciBtZXNvZGVybSB1bWFwIHVwcmVnfQojIFBsb3QgVU1BUCBzaG93aW5nIEVOU0cwMDAwMDExNTc2MiBleHByZXNzaW9uIGFjcm9zcyBkaWFnbm9zaXMgZ3JvdXBzCnNjYXRlcjo6cGxvdFJlZHVjZWREaW0obWVzb2Rlcm1fc2NlLAogICAgICAgICAgICAgICAgICAgICAgIGRpbXJlZCA9ICJmYXN0bW5uX1VNQVAiLAogICAgICAgICAgICAgICAgICAgICAgIGNvbG9yX2J5ID0gIkVOU0cwMDAwMDExNTc2MiIsCiAgICAgICAgICAgICAgICAgICAgICAgb3RoZXJfZmllbGRzID0gImRpYWdub3Npc19ncm91cCIpICsKICBmYWNldF93cmFwKHZhcnMoZGlhZ25vc2lzX2dyb3VwKSkgKwogIHRoZW1lX2J3KCkKYGBgCgoKCldlIGNhbiBhbHNvIHZpc3VhbGl6ZSB0aGlzIGRhdGEgdXNpbmcgdmlvbGluIHBsb3RzIHdpdGggdGhlIGBzY2F0ZXI6OnBsb3RFeHByZXNzaW9uKClgIGZ1bmN0aW9uLCBhbGxvd2luZyB1cyB0byBjb21wYXJlIGV4cHJlc3Npb24gYWNyb3NzIHN1YnBvcHVsYXRpb25zIG9mIGludGVyZXN0IChoZXJlLCB0dW1vciBjZWxsIHR5cGVzIGFuZCBkaWFnbm9zaXMgZ3JvdXBzKS4KCkxldCdzIG1ha2UgdGhhdCBwbG90IGJlbG93IGZyb20gdGhlIGBybXNfc2NlYCBvYmplY3QsIHdoaWNoIHlvdSdsbCByZWNhbGwgd2UgcHJldmlvdXNseSBmaWx0ZXJlZCB0byBvbmx5IHR1bW9yIGNlbGxzLgpVc2UgYGZhY2V0X2dyaWQoKWAgaW4geW91ciBwbG90IGJlbG93IHRvIHNob3cgc2VwYXJhdGUgcGFuZWxzIGZvciBlYWNoIGNvbWJpbmF0aW9uIG9mIGNlbGwgdHlwZSBhbmQgZGlhZ25vc2lzIGdyb3VwLgoKYGBge3IgcGxvdEV4cHJlc3Npb24gcGxvdCwgc29sdXRpb24gPSBUUlVFfQojIERlZmluZSBhIHZlY3RvciBvZiBFbnNlbWJsIGlkcyB0byBwbG90CgojIENyZWF0ZSBmYWNldGVkIHZpb2xpbiBwbG90CgpgYGAKCkJhc2VkIG9uIHRoaXMgcGxvdCwgaXQgbG9va3MgbGlrZSB0aGUgZXhwcmVzc2lvbiBkaWZmZXJlbmNlcyBhbW9uZyBkaWFnbm9zaXMgZ3JvdXBzIHdlIGZvdW5kIGZvciB0dW1vciBtZXNvZGVybSBjZWxscyBfY291bGRfIGJlIGFuIG92ZXJhbGwgdHJlbmQgYWNyb3NzIGNhbmNlciBjZWxsIHR5cGVzLgpJZiB3ZSB3YW50ZWQgdG8gZm9ybWFsbHkgdGVzdCB0aGlzLCB3ZSBjb3VsZCBjb25zaWRlciBhIG11bHRpdmFyaWF0ZSBtb2RlbCB0aGF0IGFjY291bnRzIGZvciBib3RoIGNlbGwgdHlwZSBhbmQgZGlhZ25vc2lzIGdyb3VwIGF0IG9uY2UsIHJhdGhlciB0aGFuIHBlcmZvcm1pbmcgYSBzZXBhcmF0ZWx5IHRlc3RpbmcgZWFjaCBjZWxsIHR5cGUuCgpMZXQncyBnbyBhaGVhZCBhbmQgc2F2ZSB0aGUgYGRlc2VxX21lc29kZXJtX3Jlc3VsdHNgIGRhdGEgZnJhbWUgdG8gb3VyIG91dHB1dCBUU1YgZmlsZS4KCmBgYHtyIGV4cG9ydCBtZXNvZGVybSByZXN1bHRzLCBzb2x1dGlvbiA9IFRSVUV9CiMgV3JpdGUgb3V0IHRoZSByZXN1bHRzIFRTViBmaWxlCgpgYGAKCgojIyBQYXJ0IEM6IENvbXBhcmUgcmVzdWx0cyB0byB0dW1vciBteW9ibGFzdCBERSBhbmFseXNpcyByZXN1bHRzCgpEdXJpbmcgaW5zdHJ1Y3Rpb24sIHdlIHBlcmZvcm1lZCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBvbiB0dW1vciBteW9ibGFzdCBjZWxscyB0byBkZXRlY3QgZGlmZmVyZW5jZXMgYmV0d2VlbiBBUk1TIGFuZCBFUk1TIHNhbXBsZXMuCldlIG1pZ2h0IHdvbmRlciBob3cgb3VyIHJlc3VsdHMgZm9yIHR1bW9yIG1lc29kZXJtIGNlbGxzIGNvbXBhcmUgdG8gdGhvc2UgZm9yIG15b2JsYXN0cy4KQmVmb3JlIHdlIGNvbXBhcmUgc29tZSBvZiB0aGVzZSBmaW5kaW5ncywgd2UgbmVlZCB0byBiZSBhd2FyZSBvZiBhIGZldyBjYXZlYXRzOgoKLSBBcyBtZW50aW9uZWQgYWJvdmUsIGlmIHdlIHdlcmUgcmVhbGx5IGludGVyZXN0ZWQgaW4gd2hldGhlciBBUk1TIGFuZCBFUk1TIHNhbXBsZXMgaGF2ZSBkaWZmZXJlbnQgZXhwcmVzc2lvbiBfYWNyb3NzIGFueSBncm91cCBvZiBjZWxsIHR5cGVzXywgd2Ugd291bGQgaGF2ZSBsaWtlZCB0byBzdGFydCB3aXRoIGEgbXVsdGl2YXJpYXRlIG1vZGVsLgpCeSBwZXJmb3JtaW5nIHNlcGFyYXRlIHRlc3RzIGZvciBzZXBhcmF0ZSBjZWxsIHR5cGVzLCB3ZSByaXNrIG1ha2luZyBjb21wYXJpc29ucyBiYXNlZCBvbiAic2lnbmlmaWNhbmNlLCIgZm9yIGV4YW1wbGUgY29uY2x1ZGluZyAidGhpcyBnZW5lIHdhcyBzaWduaWZpY2FudCBpbiBtZXNvZGVybSBidXQgbm90IG15b2JsYXN0LCB0aGVyZWZvcmUgaXQncyBvbmx5IGltcG9ydGFudCBpbiBtZXNvZGVybSBjZWxsIHR5cGVzLiIKU2lnbmlmaWNhbmNlIGFzIGEgcHJvcGVydHkgaW4tYW5kLW9mLWl0c2VsZiByZWxpZXMgb24gYXJiaXRyYXJ5IFAtdmFsdWUgY3V0b2ZmcywgYW5kIFAtdmFsdWVzIGFjcm9zcyBkaWZmZXJlbnQgdGVzdHMgYW5kIHNhbXBsZXMgYXJlIG5vdCBjcmVhdGVkIGVxdWFsbHkhClRoaXMgbWVhbnMgeW91IGRvIG5vdCB3YW50IHRvIHJlbHkgb24gdGhlc2Uga2luZHMgb2YgY29tcGFyaXNvbnMgdG8gZHJhdyBjb25jbHVzaW9ucywgYnV0IHRoZXkgY2FuIGJlIHVzZWZ1bCBmb3IgZXhwbG9yYXRpb24sIGFzIHdlIHdpbGwgZG8gaGVyZS4KCi0gRHVyaW5nIGFsbCBvZiBvdXIgREUgdGVzdHMsIHRoZXJlJ3MgYSBnb29kIGNoYW5jZSB0aGF0IHdlIG1heSBoYXZlIGNvbW1pdHRlZCBzb21lIGxpZ2h0IGNpcmN1bGFyaXR5OgpUaGUgY2VsbCB0eXBlcyB3ZSBhcmUgdXNpbmcgZnJvbSBbUGF0ZWwgX2V0IGFsLl8gKDIwMjIpXShodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLmRldmNlbC4yMDIyLjA0LjAwMykgd2VyZSBkZXRlcm1pbmVkIGZyb20gZXhwcmVzc2lvbiBkYXRhLgpCdXQgdGhlbiB3ZSB1c2VkIHRoZXNlIGV4cHJlc3Npb24tZGVyaXZlZCBjZWxsIHR5cGVzIHRvIGZpbHRlciBmb3IgREUgdGVzdGluZywgYW5kIHRoZW4gd2UgdGVzdGVkIGZvciBkaWZmZXJlbmNlcyBpbiwgeW91IGd1ZXNzZWQgaXQsIGV4cHJlc3Npb24hCkl0J3Mgbm90IG5lY2Vzc2FyaWx5IHBvc3NpYmxlIHRvIGF2b2lkIHRoaXMgdHJhcCAoc2luY2Ugd2UgbmVlZCB0byB1c2UgX3NvbWV0aGluZ18gdG8gYW5ub3RhdGUgY2VsbCB0eXBlcyksIGJ1dCBpdCdzIGltcG9ydGFudCB0byBiZSBhd2FyZSB0aGF0IHRoaXMgaXMgYSBjb25mb3VuZGluZyBmYWN0b3IgYWNyb3NzIG91ciByZXN1bHRzLgoKQmVhcmluZyB0aGVzZSBjYXZlYXRzIGluIG1pbmQsIGxldCdzIGdvIGFoZWFkIGFuZCBjb21wYXJlIHJlc3VsdHMgYSBiaXQuCkF0IHRoZSBiZWdpbm5pbmcgb2YgdGhpcyBub3RlYm9vaywgd2UgcmVhZCBpbiB0aGUgdHVtb3IgbXlvYmxhc3QgREUgcmVzdWx0cyBhbmQgc2F2ZWQgaXQgYXMgYGRlc2VxX215b2JsYXN0X3Jlc3VsdHNgLgpMZXQncyByZW1pbmQgb3Vyc2VsdmVzIHdoYXQgdGhpcyBkYXRhIGZyYW1lIGxvb2tzIGxpa2U6CgoKYGBge3IgcHJpbnQgZGVzZXFfbXlvYmxhc3RfcmVzdWx0c30KaGVhZChkZXNlcV9teW9ibGFzdF9yZXN1bHRzKQpgYGAKClRoZSBkYXRhIGZyYW1lIGhhcyB0aGUgc2FtZSBjb2x1bW5zIGFzIG91ciBtZXNvZGVybSByZXN1bHRzIHN0b3JlZCBpbiBgZGVzZXFfbWVzb2Rlcm1fcmVzdWx0c2AuClRvIGNvbXBhcmUgcmVzdWx0cyBtb3JlIGVhc2lseSwgd2UnZCBsaWtlIHRvIGhhdmUgdGhlbSBhbGwgaW4gYSBzaW5nbGUgZGF0YSBmcmFtZSBmb3JtYXR0ZWQgdG8gZmFjaWxpdGF0ZSBjb21wYXJpc29uLgpGb3IgZXhhbXBsZSwgd2UgY2FuIG1ha2UgYSBkYXRhIGZyYW1lIHRoYXQgaGFzIGNvbHVtbnMgZm9yIGVhY2ggdGVzdCdzIFAtdmFsdWVzIGFuZCBsb2cyLWZvbGQgY2hhbmdlcyBzbyB3ZSBjYW4gZGlyZWN0bHkgY29tcGFyZSB0aGUgcmVzdWx0cyBmb3IgZWFjaCBnZW5lLgpMZXQncyB3cmFuZ2xlIHRoYXQgdG9nZXRoZXI6CgpgYGB7ciBjb21iaW5lIHRlc3QgcmVzdWx0c30KIyBzZWxlY3QgZnJvbSBhbmQgcmVuYW1lIG15b2JsYXN0IHJlc3VsdHMKbXlvYmxhc3RfZGYgPC0gZGVzZXFfbXlvYmxhc3RfcmVzdWx0cyB8PgogIGRwbHlyOjpzZWxlY3QoZW5zZW1ibF9pZCwKICAgICAgICAgICAgICAgIGdlbmVfc3ltYm9sLAogICAgICAgICAgICAgICAgbXlvYmxhc3RfcGFkaiA9IHBhZGosCiAgICAgICAgICAgICAgICBteW9ibGFzdF9sb2cyRm9sZENoYW5nZSA9IGxvZzJGb2xkQ2hhbmdlKQoKIyBzZWxlY3QgJiByZW5hbWUgbWVzb2Rlcm0gcmVzdWx0Cm1lc29kZXJtX2RmIDwtIGRlc2VxX21lc29kZXJtX3Jlc3VsdHMgfD4KICBkcGx5cjo6c2VsZWN0KGVuc2VtYmxfaWQsCiAgICAgICAgICAgICAgICBnZW5lX3N5bWJvbCwKICAgICAgICAgICAgICAgIG1lc29kZXJtX3BhZGogPSBwYWRqLAogICAgICAgICAgICAgICAgbWVzb2Rlcm1fbG9nMkZvbGRDaGFuZ2UgPSBsb2cyRm9sZENoYW5nZSkKCiMgQ29tYmluZSBteW9ibGFzdCBhbmQgbWVzb2Rlcm0gcmVzdWx0cwpkZXNlcV9yZXN1bHRzX2FsbCA8LSBkcGx5cjo6aW5uZXJfam9pbihteW9ibGFzdF9kZiwgbWVzb2Rlcm1fZGYpCgojIFByaW50IHRoZSBjb21iaW5lZCByZXN1bHQgZGF0YSBmcmFtZQpkZXNlcV9yZXN1bHRzX2FsbApgYGAKCldlIGNhbiBub3cgZGlyZWN0bHkgY29tcGFyZSByZXN1bHRzIG9uIGEgcGVyLWdlbmUgYmFzaXMhCk9uZSB3YXkgd2UgY2FuIGV4cGxvcmUgdGhlIHJlc3VsdCBkaWZmZXJlbmNlcyBpcyB3aXRoIGEgc2NhdHRlcnBsb3Qgc2hvd2luZyBsb2cyLWZvbGQgY2hhbmdlcyBmb3IgZWFjaCBjZWxsIHR5cGUuCldlIGNhbiBhbHNvIHN0eWxlIHRoZSBwb2ludHMgdG8gYmUgY29sb3JlZCBiYXNlZCBvbiB0aGVpciBvdmVyYWxsIHNpZ25pZmljYW5jZSwgdXNpbmcgb25lIGNvbG9yIGZvciBlYWNoIG9mIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczoKCiAgLSBCb3RoIGNlbGwgdHlwZXMgYXJlIHNpZ25pZmljYW50CiAgLSBPbmx5IG15b2JsYXN0IHR1bW9yIGNlbGxzIGFyZSBzaWduaWZpY2FudAogIC0gT25seSBtZXNvZGVybSB0dW1vciBjZWxscyBhcmUgc2lnbmlmaWNhbnQKICAtIE5laXRoZXIgY2VsbCB0eXBlIGlzIHNpZ25pZmljYW50CgpDb2xvcmluZyBwb2ludHMgYnkgc2lnbmlmaWNhbmNlIGluIGBnZ3Bsb3QyYCB3aWxsIHJlcXVpcmUgYSBiaXQgbW9yZSB3cmFuZ2xpbmcsIGZvciB3aGljaCB3ZSdsbCB1c2UgdGhlIGZ1bmN0aW9uIFtgZHBseXI6OmNhc2Vfd2hlbigpYF0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9jYXNlX3doZW4uaHRtbCkuCldlIHByb3ZpZGUgdGhpcyBmdW5jdGlvbiB3aXRoIGEgc2V0IG9mIGxvZ2ljYWwgZXhwcmVzc2lvbnMgYW5kIGVhY2ggYXNzaWduZWQgdmFsdWUgaXMgZGVzaWduYXRlZCBieSBgfmAuClRoZSBleHByZXNzaW9ucyBhcmUgZXZhbHVhdGVkIGluIG9yZGVyLCBzdG9wcGluZyBhdCB0aGUgX2ZpcnN0XyBvbmUgdGhhdCBldmFsdWF0ZXMgYXMgYFRSVUVgIGFuZCByZXR1cm5pbmcgdGhlIGFzc29jaWF0ZWQgdmFsdWUuCldlJ2xsIHVzZSBgZHBseXI6OmNhc2Vfd2hlbigpYCB0byBoZWxwIHVzIGNyZWF0ZSBhIG5ldyBgc2lnbmlmaWNhbmNlX3R5cGVgIGNvbHVtbiBpbiBvdXIgZGF0YSBmcmFtZSB0byBjb250YWluIHRoaXMgaW5mb3JtYXRpb24uCgpgYGB7ciBhZGQgc2lnbmlmaWNhbmNlIGNhdGVnb3JpZXN9CmRlc2VxX3Jlc3VsdHNfYWxsIDwtIGRlc2VxX3Jlc3VsdHNfYWxsIHw+CiAgIyBDcmVhdGUgYSBuZXcgY29sdW1uIGNhbGxlZCBzaWduaWZpY2FuY2VfdHlwZQogIGRwbHlyOjptdXRhdGUoc2lnbmlmaWNhbmNlX3R5cGUgPSBkcGx5cjo6Y2FzZV93aGVuKAogICAgIyBVc2UgdmFsdWUgIkJvdGgiIGlmIGJvdGggY29tcGFyaXNvbnMgYXJlIHNpZ25pZmljYW50CiAgICBteW9ibGFzdF9wYWRqIDw9IDAuMDUgJiBtZXNvZGVybV9wYWRqIDw9IDAuMDUgfiAiQm90aCIsCiAgICAjIFVzZSB2YWx1ZSAiTXlvYmxhc3Qgb25seSIgaWYgb25seSBteW9ibGFzdCBpcyBzaWduaWZpY2FudAogICAgbXlvYmxhc3RfcGFkaiA8PSAwLjA1IH4gIk15b2JsYXN0IG9ubHkiLAogICAgIyBVc2UgdmFsdWUgIk1lc29kZXJtIG9ubHkiIGlmIG9ubHkgbWVzb2Rlcm0gaXMgc2lnbmlmaWNhbnQKICAgIG1lc29kZXJtX3BhZGogPD0gMC4wNSB+ICJNZXNvZGVybSBvbmx5IiwKICAgICMgRGVmYXVsdCBjYXNlOiBuZWl0aGVyIGNvbXBhcmlzb24gaXMgc2lnbmlmaWNhbnQKICAgICMgIFRoaXMgd2lsbCBjYXB0dXJlIGFsbCByZW1haW5pbmcgcm93cyBub3QgY292ZXJlZCBieQogICAgIyAgdGhlIHByZXZpb3VzIGNvbmRpdGlvbnMKICAgIFRSVUUgfiAiTmVpdGhlciIKICApKSB8PgogICMgTW92ZSB0aGUgbmV3IGNvbHVtbiB0byB0aGUgZnJvbnQgb2YgdGhlIGRhdGEgZnJhbWUKICAjICBzbyB3ZSBjYW4gZWFzaWx5IHNlZSBpdCB3aGVuIHByaW50aW5nCiAgZHBseXI6OnNlbGVjdChzaWduaWZpY2FuY2VfdHlwZSwgZXZlcnl0aGluZygpKQoKIyBQcmludCB0aGUgdXBkYXRlZCBkYXRhIGZyYW1lCmRlc2VxX3Jlc3VsdHNfYWxsCmBgYAoKTGV0J3MgaGF2ZSBhIHF1aWNrIGxvb2sgYXQgdGhlIG5ldyBgc2lnbmlmaWNhbmNlX3R5cGVgIHZhcmlhYmxlIGJ5IG1ha2luZyBhIGB0YWJsZSgpYCBvZiBpdHMgdmFsdWVzOgoKYGBge3IgdGFibGUgc2lnbmZpY2FuY2VfdHlwZSwgc29sdXRpb24gPSBUUlVFfQojIE1ha2UgYSB0YWJsZSBvZiBzaWduaWZpY2FuY2UgdHlwZXMKCmBgYAoKTW9zdCBnZW5lcyBhcmUgbm90IHNpZ25pZmljYW50IGluIGVpdGhlciBjb21wYXJpc29uLCBhbmQgdGhlIG15b2JsYXN0IHRlc3QgZGV0ZWN0ZWQgbW9yZSBzaWduaWZpY2FudCBnZW5lcyBjb21wYXJlZCB0byB0aGUgbWVzb2Rlcm0gdGVzdC4KQnV0IHRoZXJlIGlzIHN0aWxsIHF1aXRlIGEgYml0IG9mIG92ZXJsYXAgaW4gdGhlIGdlbmVzIGlkZW50aWZpZWQgYXMgc2lnbmlmaWNhbnQgaW4gYm90aCB0ZXN0cyEKCk5vdyB3ZSBjYW4gbWFrZSBvdXIgc2NhdHRlcnBsb3Q6CgoKYGBge3IgbG9nZmMgc2NhdHRlcnBsb3R9CiMgUGxvdCBsb2cyIGZvbGQgY2hhbmdlcyBhZ2FpbnN0IGVhY2ggb3RoZXIKZ2dwbG90KGRlc2VxX3Jlc3VsdHNfYWxsLAogICAgICAgYWVzKHggPSBteW9ibGFzdF9sb2cyRm9sZENoYW5nZSwKICAgICAgICAgICB5ID0gbWVzb2Rlcm1fbG9nMkZvbGRDaGFuZ2UsCiAgICAgICAgICAgY29sb3IgPSBzaWduaWZpY2FuY2VfdHlwZSkpICsKICAjIFN0eWxpbmcgdG8gc2VlIHBvaW50cwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwKICAgICAgICAgICAgIGFscGhhID0gMC41KSArCiAgIyBVc2UgdGhlIGNvbG9yYmxpbmQtZnJpZW5kbHkgdmlyaWRpcyBjb2xvciBzY2FsZQogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZCgpICsKICAjIFNldCBwbG90IGxhYmVscwogIGxhYnMoCiAgICB4ID0gIk15b2JsYXN0IGxvZzIgZm9sZCBjaGFuZ2UiLAogICAgeSA9ICJNZXNvZGVybSBsb2cyIGZvbGQgY2hhbmdlIiwKICAgIGNvbG9yID0gIlNpZ25maWNhbmNlIFR5cGUiCiAgKSArCiAgdGhlbWVfYncoKSArCiAgIyBNYWtlIGxlZ2VuZCBwb2ludHMgbGFyZ2VyIGFuZCBlYXNpZXIgdG8gc2VlCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDIpKSkgKwogICMgU2V0IGVxdWFsIHggYW5kIHkgc2NhbGVzIGZvciBlYXNpZXIgY29tcGFyaXNvbiBiZXR3ZWVuIGF4ZXMKICBjb29yZF9lcXVhbCgpCmBgYAoKCldoYXQga2luZCBvZiB0cmVuZHMgZG8gd2Ugc2VlIGhlcmU/CgotIFRoZSBtaWRkbGUgb2YgdGhlIHBsb3QgY2xvc2UgdG8gY29vcmRpbmF0ZXMgYCgwLDApYCBoYXMgYSBsYXJnZSBjbG91ZCBvZiB5ZWxsb3cgIk5laXRoZXIiIHBvaW50cywgd2hpY2ggbWFrZXMgc2Vuc2UuCi0gQWxvbmcgZWFjaCBsaW5lIGB4ID0gMGAgb3IgYHkgPSAwYCwgd2Ugc2VlIHN0cmlwcyBvZiBwb2ludHMgZm9yIGdlbmVzIHRoYXQgYXJlIG9ubHkgc2lnbmlmaWNhbnQgZm9yIG9ubHkgb25lIGNlbGwgdHlwZS4KRm9yIGV4YW1wbGUsIHRoZSBibHVlICJNZXNvZGVybSBvbmx5IiBwb2ludHMgbW9zdGx5IGZvbGxvdyBgeCA9IDBgOyBmb3IgdGhlc2UgcG9pbnRzLCB0aGUgYXNzb2NpYXRlZCBteW9ibGFzdCBsb2cyLWZvbGQgY2hhbmdlcyBhcmUgYWxsIGF0IGFib3V0IDAuCi0gRmluYWxseSwgd2UgaGF2ZSB0aGUgcHVycGxlICJCb3RoIiBwb2ludHMsIHdoaWNoIGFyZSBwcmVzZW50IGluIGVhY2ggcXVhZHJhbnQgZmFydGhlciBhd2F5IGZyb20gYCgwLDApYCwgZGVlbWVkIHNpZ25pZmljYW50IGluIGJvdGggREUgdGVzdHMuCk1vc3Qgb2YgdGhlc2UgcG9pbnRzIGFyZSBpbiB0aGUgdG9wIHJpZ2h0IGFuZCBib3R0b20gbGVmdCBxdWFkcmFudHMsIGNvcnJlc3BvbmRpbmcgdG8gY29uZGl0aW9ucyB3aGVyZSBfYm90aF8gY2VsbCB0eXBlcyBzaG93IHNpZ25pZmljYW50IHVwcmVndWxhdGlvbiBvciBkb3ducmVndWxhdGlvbiwgcmVzcGVjdGl2ZWx5LgpIb3dldmVyLCB3ZSBkbyBhbHNvIHNlZSBzb21lIHB1cnBsZSBwb2ludHMsIGFsYmVpdCBmZXdlciwgaW4gdGhlIHRvcCBsZWZ0IGFuZCBib3R0b20gcmlnaHQgcXVhZHJhbnRzIHdoaWNoIHJlcHJlc2VudCBnZW5lcyB3aGVyZSBib3RoIGNlbGwgdHlwZXMgc2hvdyBzaWduaWZpY2FudCBleHByZXNzaW9uIGRpZmZlcmVuY2VzIGJ1dCBpbiBfZGlmZmVyZW50XyBkaXJlY3Rpb25zLgoKVG8gc3RhcnQgZXhwbG9yaW5nLCBsZXQncyBwaWNrIG91dCBzb21lIG9mIHRoZSBnZW5lcyB3aXRoIHRoZSBsYXJnZXN0IGZvbGQtY2hhbmdlcyBmcm9tIHRoZSB0b3AgcmlnaHQgYW5kIGJvdHRvbSBsZWZ0IHF1YWRyYW50cyBvZiB0aGUgcGxvdC4KQmFzZWQgb24gdGhlIHBsb3QsIGl0IGxvb2tzIGxpa2Ugd2UnbGwgZmluZCBhIHNldCBvZiBzaGFyZWQgdXByZWd1bGF0ZWQgZ2VuZXMgYnkgbG9va2luZyBmb3IgZXhwcmVzc2lvbiB2YWx1ZXMgdGhhdCBhcmUgZ3JlYXRlciB0aGFuIDggZm9yIGJvdGggY2VsbCB0eXBlcywgYW5kIGRvd25yZWd1bGF0ZWQgZ2VuZXMgYnkgbG9va2luZyBmb3IgZXhwcmVzc2lvbiB2YWx1ZXMgbGVzcyB0aGFuICAtMTAgZm9yIG15b2JsYXN0cyBhbmQgLTcgZm9yIG1lc29kZXJtIGNlbGxzLgoKCmBgYHtyIGZpbmQgcXVhZHJhbnQgZ2VuZXN9CiMgRmluZCBnZW5lcyBpbiB0b3AgcmlnaHQgYW5kIGJvdHRvbSBsZWZ0IHF1YWRyYW50cwpkZXNlcV9yZXN1bHRzX2FsbCB8PgogIGRwbHlyOjpmaWx0ZXIoCiAgICAjIEZpbmQgZWl0aGVyIHRoZSBoaWdobHkgdXByZWd1bGF0ZWQgZ2VuZXM6CiAgICAobXlvYmxhc3RfbG9nMkZvbGRDaGFuZ2UgPj0gOCAmIG1lc29kZXJtX2xvZzJGb2xkQ2hhbmdlID49IDgpIHwKICAgICAjIE9SLCB0aGUgaGlnaGx5IGRvd25yZWd1bGF0ZWQgZ2VuZXM6CiAgICAobXlvYmxhc3RfbG9nMkZvbGRDaGFuZ2UgPD0gLTEwICYgbWVzb2Rlcm1fbG9nMkZvbGRDaGFuZ2UgPD0tNykKICApIHw+CiAgIyBhcnJhbmdlIG9uIG15b2JsYXN0X2xvZzJGb2xkQ2hhbmdlCiAgZHBseXI6OmFycmFuZ2UobXlvYmxhc3RfbG9nMkZvbGRDaGFuZ2UpCmBgYAoKU29tZXRoaW5nIHlvdSdsbCBzZWUgaW4gdGhlc2UgcmVzdWx0cyBhcmUgc29tZSBwcmV0dHkgZGlmZmVyZW50IFAtdmFsdWVzIGJldHdlZW4gY2VsbCB0eXBlcyAoYWxzbyBub3RlIHRoYXQgdGhlIGBOQWAgZ2VuZXMgaGVyZSBhcmUgbG5jUk5BcyB3aXRoIG5vIGZvcm1hbGx5IGFzc2lnbmVkIGdlbmUgc3ltYm9sKS4KU3BlY2lmaWNhbGx5LCB0aGUgbXlvYmxhc3QgUC12YWx1ZXMgYXJlIGFsbCAzLTcgb3JkZXJzIG9mIG1hZ25pdHVkZSBsb3dlciB0aGFuIHRoZWlyIG1lc29kZXJtIGNvdW50ZXJwYXJ0cywgd2hpY2ggX21heV8gYmUgYSByZXN1bHQgb2YgdGhlIHJlbGF0aXZlbHkgaGlnaGVyIHNhbXBsZSBzaXplIGZvciBteW9ibGFzdCB0ZXN0cyAtIGxhcmdlciBzYW1wbGUgc2l6ZXMgbGVhZCB0byBtb3JlIGV4dHJlbWUgUC12YWx1ZXMuCkltcG9ydGFudGx5LCB3ZSBkbyBfbm90XyB3YW50IHRvIGNvbXBhcmUgdGhlc2UgUC12YWx1ZXMgZGlyZWN0bHkgYW5kIGNvbmNsdWRlIHRoYXQgYSBnaXZlbiBnZW5lIHdhcyAibW9yZSBvciBsZXNzIHNpZ25pZmljYW50IiBpbiBvbmUgY2VsbCB0eXBlIG9yIGFub3RoZXIsIHNpbmNlIFAtdmFsdWVzIGNhbm5vdCBiZSBjb21wYXJlZCBhY3Jvc3MgdGVzdHMgKGFnYWluLCBmb3IgYSBtb3JlIHJvYnVzdCBhc3Nlc3NtZW50IG9mIGRpZmZlcmVudGlhbCBleHByZXNzaW9uLCB1c2UgYSBtdWx0aXZhcmlhdGUgbW9kZWwgdGhhdCBhY2NvdW50cyBmb3IgY2VsbCB0eXBlcyEpLgoKVG8gd3JhcCB1cCwgZmVlbCBmcmVlIHRvIHBlcmZvcm0gc29tZSBxdWljayB2aXN1YWxpemF0aW9uIG9mIHNvbWUgb2YgdGhlc2UgZ2VuZXMhCgpBbHRlcm5hdGl2ZWx5IChvciBpbiBhZGRpdGlvbiEpLCB5b3UgY2FuIHZpc3VhbGl6ZSB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gb2YgZ2VuZXMgdGhhdCByZWd1bGF0ZSBteW9nZW5lc2lzLCBhcyBzaG93biBpbiB0aGUgZmlndXJlIGZyb20gUGF0ZWwgX2V0IGFsLl8gYXQgdGhlIHRvcCBvZiB0aGlzIG5vdGVib29rLgpMZXQncyBoYXZlIGEgcXVpY2sgbG9vayBhdCB0aG9zZSByZXN1bHRzOgoKYGBge3IgbXlvZ2VuZXNpcyBnZW5lc30KIyBEZWZpbmUgdmVjdG9yIG9mIG15b2dlbmVzaXMgZ2VuZSBzeW1ib2xzCm15b2dlbmVzaXNfc3ltYm9scyA8LSBjKCJNRU9YMiIsICJQQVgzIiwgIk1ZRjUiLCAiTVNDIiwgIk1ZT0ciLCAiTUVGMkMiKQoKIyBQcmludCB0aGUgREUgcmVzdWx0cyBmb3IgdGhvc2UgZ2VuZXMKZGVzZXFfcmVzdWx0c19hbGwgfD4KICBkcGx5cjo6ZmlsdGVyKGdlbmVfc3ltYm9sICVpbiUgbXlvZ2VuZXNpc19zeW1ib2xzKSB8PgogICMgc29ydCBpbiBvcmRlciBvZiB0aGUgYG15b2dlbmVzaXNfc3ltYm9sc2AgdmVjdG9yCiAgZHBseXI6OmFycmFuZ2UobWF0Y2goZ2VuZV9zeW1ib2wsIG15b2dlbmVzaXNfc3ltYm9scykpCmBgYAoKWW91IGNhbiBtYWtlIHBsb3RzIHdlJ3ZlIHNlZW4gbGlrZS4uLgoKLSBBIGZhY2V0ZWQgVU1BUCB3aXRoIGBzY2F0ZXI6OnBsb3RVTUFQKClgIHNob3dpbmcgZXhwcmVzc2lvbiBhY3Jvc3MgY2VsbCB0eXBlIGFuZCBkaWFnbm9zaXMgZ3JvdXAKLSBBIHZpb2xpbiBwbG90IHdpdGggYHNjYXRlcjo6cGxvdEV4cHJlc3Npb24oKWAgZmFjZXRlZCBhY3Jvc3MgY2VsbCB0eXBlIGFuZCBkaWFnbm9zaXMgZ3JvdXAKCgpgYGB7ciBkaXkgcGxvdHMsIHNvbHV0aW9uID0gVFJVRX0KIyBVc2UgdGhpcyBjaHVuayAob3IgYWRkIG1vcmUgY2h1bmtzISkgdG8gbWFrZSB5b3VyIG93biBwbG90cwoKIyB2ZWN0b3Igb2YgZ2VuZSBzeW1ib2xzIG5hbWVkIGJ5IEVuc2VtYmwgaWQKCmBgYAoKIyMgU2Vzc2lvbiBJbmZvCgpgYGB7ciBzZXNzaW9uaW5mb30Kc2Vzc2lvbkluZm8oKQpgYGAK